为什么这个在 for 循环中复制文件的命令在 bash 中有效,但在 zsh 中无效?

val*_*rus 3 bash zsh for

我试图将一堆具有相同名称但位于不同子目录中的文件复制到一个目录中,根据原始文件的路径将名称更改为名称。我使用了一个 for 循环,它在 bash 中执行我的意图,但在 zsh 中表现得非常奇怪。

命令(为了易读性添加了换行符):

for f in */Flavors/*/stuff1/filename.txt;
    do l=$(echo $f | cut -d'/' --output-delimiter '' -f1,3);
    dest=/stuff2/${l}.utf8.txt;
    echo $f $dest;
    cp -v -- $f $dest;
done
Run Code Online (Sandbox Code Playgroud)

在 zsh 中输出。我打算输出文件名是例如“EnglishAU.utf8.txt”,但它们只是“English”、“French”和“Spanish”。请注意,echo循环显示$dest包含正确的路径,然后cp使用错误的路径!

English/Flavors/AU/stuff1/filename.txt /stuff2/EnglishAU.utf8.txt
`English/Flavors/AU/stuff1/filename.txt' -> `/stuff2/English'
English/Flavors/UK/stuff1/filename.txt /stuff2/EnglishUK.utf8.txt
`English/Flavors/UK/stuff1/filename.txt' -> `/stuff2/English'
English/Flavors/US/stuff1/filename.txt /stuff2/EnglishUS.utf8.txt
`English/Flavors/US/stuff1/filename.txt' -> `/stuff2/English'
French/Flavors/CA/stuff1/filename.txt /stuff2/FrenchCA.utf8.txt
`French/Flavors/CA/stuff1/filename.txt' -> `/stuff2/French'
French/Flavors/FR/stuff1/filename.txt /stuff2/FrenchFR.utf8.txt
`French/Flavors/FR/stuff1/filename.txt' -> `/stuff2/French'
Spanish/Flavors/ES/stuff1/filename.txt /stuff2/SpanishES.utf8.txt
`Spanish/Flavors/ES/stuff1/filename.txt' -> `/stuff2/Spanish'
Spanish/Flavors/OT/stuff1/filename.txt /stuff2/SpanishOT.utf8.txt
`Spanish/Flavors/OT/stuff1/filename.txt' -> `/stuff2/Spanish'
Run Code Online (Sandbox Code Playgroud)

如上所述,这在 bash 中按预期工作。zsh 在做什么?

phe*_*mer 8

发生这种情况是因为cut在输出中输出 NULL 字符。您不能传递包含空字符的程序参数(请参阅)。

在 bash 中,这是有效的,因为 bash 无法处理字符串中的 NULL 字符,并且会将它们去掉。Zsh 更强大一点,它可以处理 NULL 字符。然而,当需要将字符串传递给程序时,它仍然包含 null,这表示参数结束。

让我们详细看看这个。

$ echo 'English/Flavors/AU/stuff1/filename.txt' | cut -d'/' --output-delimiter '' -f1,3 | xxd
0000000: 456e 676c 6973 6800 4155 0a              English.AU.
Run Code Online (Sandbox Code Playgroud)

在这里,我们模拟了您的一个文件,通过cut. 请注意xxdEnglish和之间有一个 NULL 字符的输出AU

现在让我们运行并模拟脚本的其余部分。

$ l=$(echo 'English/Flavors/AU/stuff1/filename.txt' | cut -d'/' --output-delimiter '' -f1,3)
$ dest=/stuff2/${l}.utf8.txt
$ echo "$dest" | xxd
0000000: 2f73 7475 6666 322f 456e 676c 6973 6800  /stuff2/English.
0000010: 4155 2e75 7466 382e 7478 740a            AU.utf8.txt.
Run Code Online (Sandbox Code Playgroud)

注意English.后面的 NULL 。该echo显示器是正确的,因为echo是内置了一个壳。如果我们使用 external echo,它也会出现这个问题。

$ /bin/echo "$dest" | xxd 
0000000: 2f73 7475 6666 322f 456e 676c 6973 680a  /stuff2/English.
Run Code Online (Sandbox Code Playgroud)

PS你真的应该引用:-)


解决办法是不使用cutawk而是使用。

$ echo 'English/Flavors/AU/stuff1/filename.txt' | awk -F/ '{ print $1$3 }' | xxd
0000000: 456e 676c 6973 6841 550a                 EnglishAU.
Run Code Online (Sandbox Code Playgroud)