shopt:“&&”有什么问题?

Oli*_*ons 5 linux bash shell

我不明白为什么当我单独做它们时它会起作用但&&它不会(这不是(像这里问题)......我错过了什么?

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 $ 
Run Code Online (Sandbox Code Playgroud)

Gor*_*son 11

由于第一个版本都在一行上,shell 必须在执行任何一个之前解析整个内容。但**/*.@(jpg|jpeg|png|gif)只有在执行之后shopt -s extglob才是有效的语法......这是在该行通过解析阶段之后。

如果这必须是单线,我不知道解决它的好方法。但是您应该能够使用大括号扩展而不是扩展全局作弊,并修改测试文件:

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
Run Code Online (Sandbox Code Playgroud)

请注意,由于globstar在扩展通配符时生效,而不是在初始解析过程中生效,因此此问题不适用于它。

说明:bash 在扩展通配符之前会进行大括号扩展,所以

for f in **/*.{jpg,jpeg,png,gif};
Run Code Online (Sandbox Code Playgroud)

扩展到

for f in **/*.jpg **/*.jpeg **/*.png **/*.gif;
Run Code Online (Sandbox Code Playgroud)

...然后这些通配符模式中的每一个都被单独扩展。这有一个潜在的问题:如果没有至少一个文件与四种模式中的每一种匹配,则不匹配的模式将作为一种虚假的占位符单独留下。

例如,如果只有 .jpg 和 .png 文件,则完全展开的列表可能包含如下内容:

path/to/image1.jpg
path/to/image2.jpg
**/*.jpeg
path/to/image3.png
**/*.gif
Run Code Online (Sandbox Code Playgroud)

...它会继续运行每个循环,包括**/*.jpeg**/*.gif。这就是为什么我必须将循环内的测试修改为

[[ -f "$f" && ! -f "$f.webp" ]] && cwebp ...
Run Code Online (Sandbox Code Playgroud)

-f "$f"测试将失败的未展开的通配符,并防止它试图使不存在的文件WEBP版本。您可以等效地使用它,它更接近原始测试:

[[ ! -f "$f" || -f "$f.webp" ]] || cwebp ...
Run Code Online (Sandbox Code Playgroud)

但我认为它在另一种形式中更直观。

顺便说一句,不匹配通配符问题的另一个可能解决方法是添加shopt -s nullglob,这会使不匹配的通配符消失。