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

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

我试图将一堆具有相同名称但位于不同子目录中的文件复制到一个目录,并根据原始文件的路径将名称更改为名称。我使用 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

在 zsh 中输出。我打算将输出文件名设置为“EnglishAU.utf8.txt”,但它们只是“英语”、“法语”和“西班牙语”。请注意,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'

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

答案1

发生这种情况是因为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.

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

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

$ 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.

注意 后面的 NULL English。由于是内置的 shell,echo因此可以正确显示它。echo如果我们使用外部的echo,它也会出现这个问题。

$ /bin/echo "$dest" | xxd 
0000000: 2f73 7475 6666 322f 456e 676c 6973 680a  /stuff2/English.

PS你真的应该也引用:-)


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

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

相关内容