为什么当文件名包含双引号时这个脚本会爆炸?
for i in **/*.flac; do
echo "$i";
done | parallel -j+0 --eta --joblog /tmp/parallel.log --progress --bar --colsep ' ' 'ffmpeg -i "{}" -ar 44100 -sample_fmt s16 -map 0:a -y -loglevel error -stats -hide_banner -nostats -nostdin "{.}.resampled.flac" && rm "{}" && mv "{.}.resampled.flac" "{}"'
我只在名称中带有括号的文件上收到此错误:"04. Somebody\'s Watching Me \(Single Version\).flac: No such file or directory"
答案1
这是因为 GNU 如何parallel
解析{}
(或{.}
等等)shell 代码。
通常情况下{}
嵌入shell 代码是错误的并且 shell 代码中的引用也无济于事,总能找到一个可能的字符串(在你的情况下是文件名)来破坏它。GNUparallel
是一个(唯一的?)例外,其中嵌入{}
是正确的方式,但为了让它的机制发挥作用,你一定不能在 shell 代码中引用{}
(或等等)。{.}
parallel
{}
尝试用论点代替引使用适合 shell 的正确语法。
在您的情况下,单引号字符串是 shell 代码。其中您有例如"{.}.resampled.flac"
。您使用的双引号会干扰引号parallel
添加,以使嵌入{.}
shell 代码更安全。您需要{.}.resampled.flac
没有双引号,{.}
不能被引用。如果您需要引用嵌入参数的其他部分,您可以这样做(例如可以)。从 shell 代码中的所有实例{.}".suffix with spaces.flac"
中删除双引号。用单引号括住整个 shell 代码仍然是正确的做法。"{}"
"{.}…"
考虑到上述情况,printf
对比echo
以及 *nix 中的事实路径名可以开始于-
或者可能包含换行符,我们可以修复您的代码并得到这个:
for i in **/*.flac; do
printf '%s\0' "$i";
done | parallel -0 -j+0 --eta --joblog /tmp/parallel.log --progress --bar --colsep ' ' 'ffmpeg -i {} -ar 44100 -sample_fmt s16 -map 0:a -y -loglevel error -stats -hide_banner -nostats -nostdin {.}.resampled.flac && rm -- {} && mv -- {.}.resampled.flac {}'
(旁注:你真的需要--colsep ' '
这里吗?)
Bash 中printf
有一个内置函数,可以接受任意数量的参数,而不会抛出“参数列表太长”错误。这意味着您实际上不需要循环for
。以下语法也可以使用:
printf '%s\0' **/*.flac | parallel -0 …