web-dev-qa-db-ja.com

shopt:「&&」の何が問題になっていますか?

なぜそれらを個別に実行すると機能するのかわかりませんが、&&では機能しません(これは (のような問題ではありません )...何が欠けていますか?

11:20:06 $ shopt -s extglob && shopt -s globstar && for f in **/*.@(jpg|jpeg|png|gif); do [[ -f "$f.webp" ]] || cwebp -quiet -q 80 "$f" -o "$f.webp"; done
bash: syntax error near unexpected token `('
11:20:11 $ shopt -s extglob && shopt -s globstar 
11:20:16 $ for f in **/*.@(jpg|jpeg|png|gif); do [[ -f "$f.webp" ]] || cwebp -quiet -q 80 "$f" -o "$f.webp"; doneError! Could not process file img/colorpicker/colormap.gif
11:20:23 $ 
5
Olivier Pons

最初のバージョンはすべて1行になっているので、シェルは実行する前にすべてを解析する必要があります。ただし、**/*.@(jpg|jpeg|png|gif)は有効な構文ですshopt -s extglobが実行された後 ...これは、行が解析フェーズを通過した後です。

これがワンライナーである必要がある場合、私はそれを回避する素晴らしい方法を知りません。しかし、拡張グロブの代わりにブレース展開を使用して、test-for-filesを変更することで、チートできるはずです。

shopt -s globstar && for f in **/*.{jpg,jpeg,png,gif}; do [[ -f "$f" && ! -f "$f.webp" ]] && cwebp -quiet -q 80 "$f" -o "$f.webp"; done

globstarはワイルドカードが展開されたときに有効になるため、最初の解析パスでは有効にならないため、この問題は適用されないことに注意してください。

説明:bashはワイルドカードを展開する前に展開をブレースします。

for f in **/*.{jpg,jpeg,png,gif};

に拡大する

for f in **/*.jpg **/*.jpeg **/*.png **/*.gif;

...そして、それらのワイルドカードパターンのそれぞれが個別に展開されます。これには潜在的な問題があります。4つのパターンのそれぞれに一致するファイルが1つもない場合、一致しないパターンは、一種の偽のプレースホルダーとして残されます。

たとえば、.jpgファイルと.pngファイルしかない場合、完全に展開されたリストには次のようなものが含まれます。

path/to/image1.jpg
path/to/image2.jpg
**/*.jpeg
path/to/image3.png
**/*.gif

...そして先に進み、**/*.jpeg**/*.gifを含むそれぞれのループを実行します。そのため、ループ内のテストを変更して、

[[ -f "$f" && ! -f "$f.webp" ]] && cwebp ...

-f "$f"テストは、展開されていないワイルドカードでは失敗し、存在しないファイルのwebpバージョンを作成しようとするのを防ぎます。これを同等に使用できます。これは、元のテストに近いものです。

[[ ! -f "$f" || -f "$f.webp" ]] || cwebp ...

でも他の方が直感的だったけどね。

ところで、一致しないワイルドカードの問題に対する別の可能な修正は、shopt -s nullglobを追加することです。これにより、一致しないものは消えます。

11
Gordon Davisson