forループからファイルをコピーするこのコマンドがbashでは機能しますが、zshでは機能しないのはなぜですか?

forループからファイルをコピーするこのコマンドがbashでは機能しますが、zshでは機能しないのはなぜですか?

名前は同じですが、サブディレクトリが異なる複数のファイルをディレクトリにコピーし、ソースファイルのパスに基づいて名前を変更しようとします。私はbashで意図したことを達成するためにforループを使用していますが、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」に設定しようとしましたが、「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'

上記のように、これはbashで期待どおりに機能します。 zshは何をしていますか?

答え1

これは、cut出力にNULL文字が出力されるために発生します。 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.

ここでは、ファイルの1つをシミュレートしてを渡します。出力の間にNULL文字がある cutことに注意してください。xxdEnglishAU

それでは、残りのスクリプトを実行してシミュレーションしてみましょう。

$ 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。組み込みシェルなのでecho正しく表示されます。echo外部を使用している場合echoでも、この問題が発生します。

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

PS:あなたもそれを引用する必要があります:-)


解決策はを使用するのではcutなくawk使用することです。

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

関連情報