通过 FFMPEG 查找和重新采样音频的脚本在带双引号的文件上失败

通过 FFMPEG 查找和重新采样音频的脚本在带双引号的文件上失败

为什么当文件名包含双引号时这个脚本会爆炸?

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 …

相关内容