web-dev-qa-db-ja.com

forループの `Zip`は、ファイルが存在する場合は機能するのに、存在しない場合は機能しないのはなぜですか?

いくつかのサブディレクトリを含むディレクトリがあります。 ファイルの圧縮に関する質問 があります。これには、ニーズに合わせて少し変更した回答が含まれています。

for i in */; do Zip "zips/${i%/}.Zip" "$i*.csv"; done

しかし、私は奇妙な問題に遭遇します。 zips/<name>.Zipが存在しない最初のフォルダーのセットでは、次のエラーが発生します。

Zip error: Nothing to do! (zips/2014-10.Zip)
        Zip warning: name not matched: 2014-11/*.csv

ただし、Zipステートメントをechoするだけの場合

for i in */; do echo Zip "zips/${i%/}.Zip" "$i*.csv"; done

次に、エコーされたコマンド(Zip zips/2014-10.Zip 2014-10/*.csv)を実行すると、正常に機能し、フォルダーが圧縮されます。次に、それについての楽しい部分は、元のコマンドの後続の実行が実際には初めて機能しなかったフォルダを実際に圧縮することです

この動作を自分でテストするには:

cd /tmp
mkdir -p 2016-01 2016-02 2016-03 zips
for i in 2*/; do touch "$i"/one.csv; done
for i in 2*/; do touch "$i"/two.csv; done
Zip zips/2016-03.Zip 2016-03/*.csv
for i in 2*/; do echo Zip "zips/${i%/}.Zip" "$i*.csv"; done
for i in 2*/; do Zip "zips/${i%/}.Zip" "$i*.csv"; done

エコーが次のステートメントを出力することがわかります。

Zip zips/2016-01.Zip 2016-01/*.csv
Zip zips/2016-02.Zip 2016-02/*.csv
Zip zips/2016-03.Zip 2016-03/*.csv

ただし、実際のZipコマンドは次のことを示します。

        Zip warning: name not matched: 2016-01/*.csv

Zip error: Nothing to do! (zips/2016-01.Zip)
        Zip warning: name not matched: 2016-02/*.csv

Zip error: Nothing to do! (zips/2016-02.Zip)
updating: 2016-03/one.csv (stored 0%)
updating: 2016-03/two.csv (stored 0%)

したがって、実際にはZipファイルが存在する.csvsでZipファイルを更新していますが、Zipファイルの作成時にはnotではありません。また、Zipコマンドの1つをコピーした場合:

$ Zip zips/2016-02.Zip 2016-02/*.csv
adding: 2016-02/one.csv (stored 0%)
adding: 2016-02/two.csv (stored 0%)

次に、Zip-all-the-thingsを再実行します。

for i in 2*/; do Zip "zips/${i%/}.Zip" "$i*.csv"; done

2016-02および2016-03に対して更新されることがわかります。 treeの出力は次のとおりです。

.
├── 2016-01
│   ├── one.csv
│   └── two.csv
├── 2016-02
│   ├── one.csv
│   └── two.csv
├── 2016-03
│   ├── one.csv
│   └── two.csv
└── zips
    ├── 2016-02.Zip
    └── 2016-03.Zip

また、(不)意外にも、これは問題なく機能します。

zsh -c "$(for i in 2*/; do echo Zip "zips/${i%/}.Zip" "$i*.csv"; done)"

ここで何が悪いのですか? (違いがある場合は、bashの代わりにzshを使用していることに注意してください)

7
Wayne Werner

シェルによる拡張

"$i*.csv"を囲む引用符が違いを生み出します。引用符を使用して、シェルはその文字列を「2014-11/*。csv」に展開します。その正確なファイルは存在せず、Zipはエラーを報告します。引用符がない場合、*も展開され(ファイル名展開/ "globbing"を介して)、結果のZipコマンドは、それぞれが個別の引数として一致するファイルの完全なリストになります。次のようにすると、forループ内で2番目の動作を取得できます。

for i in */ ; do Zip "zips/${i%/}.Zip" "$i"*.csv ; done

ジップによる拡張

Zipは、それ自体でワイルドカードを展開することもできますが、すべての状況で展開できるわけではありません。 Zipマニュアル から:

Zipプログラムは、変更中のZipアーカイブ内にある名前、または-x(除外)または-i(include)オプション、バックスラッシュまたは引用符を使用して、名前の展開を行わないようにシェルに指示することにより、操作するファイルのリスト。

Zipはワイルドカードを既存のアーカイブの内容と照合しようとするため、アーカイブを正常に作成した後、元のコマンドは後続の試行で機能します。それらはそこに存在し、ファイルシステムにも存在するため、updating:で報告されます。

アーカイブを作成するときにZipにワイルドカードを処理させるには、-r(recurse)オプションを使用して、要求されたディレクトリに再帰し、-i(include)を使用して、パターンに一致するファイルに制限します。

 for i in */ ; do Zip -r "zips/${i%/}.Zip" "$i" -i '*.csv' ; done
14
JigglyNaga