Bash 变量在传递到 ffmpeg 时被截断

Bash 变量在传递到 ffmpeg 时被截断

.flac我有一张包含 11 个音频文件的专辑。 (编辑:由于这个问题已经解决,现在很明显文件的确切名称和内容是无关紧要的,所以我已经重命名了它们):

> find . -name "*.flac"
./two.flac
./ten.flac
./nine.flac
./eight.flac
./seven.flac
./three.flac
./four.flac
./five.flac
./one.flac
./eleven.flac
./six.flac

我将它们转换为.wav具有特定采样率和位深度的文件。我ffmpeg在 Bash shell 中使用来执行此操作。如果我手动调用这样的命令,那么它对于 11 个文件中的每一个文件都可以完美运行:

ffmpeg -i "./six.flac" -sample_fmt s16 -ar 44100 "./six.wav"

但是,当我编写一个while循环来为每个文件运行此命令时:

find . -name "*.flac" | sed "s/\.flac$//" | while read f; do ffmpeg -i "$f.flac" -sample_fmt s16 -ar 44100 "$f.wav"; done

这有效,但仅适用于 8/11 的文件,并ffmpeg针对三个失败给出以下错误消息:

/ten.flac: No such file or directory
ree.flac: No such file or directory
ix.flac: No such file or directory

对于./ten.flac,相对文件路径中的前导.被截断,形成绝对路径,而另外两个./three.flac和 则./six.flac丢失更多字符,包括从其基本名称的开头开始。其他八个文件中的一些已被./截断,但这产生了正确的相对路径,因此ffmpeg在这些情况下能够继续。

如果我使用相同的循环结构,但使用echo "$f"而不是调用ffmpeg,我认为输出没有问题:

> find . -name "*.flac" | sed "s/\.flac$//" | while read f; do echo "$f"; done
./two
./ten
./nine
./seven
./three
./four
./five
./one
./eleven
./six

所以我确信循环的结构很好,并且"$f"在每次迭代中都扩展了我期望的方式。不知何故,当传递"$f.flac"到 时ffmpeg,部分字符串被截断,但仅限于某些文件。

我只是想了解为什么我的第一次尝试会表现出这种行为。我并不是在寻找循环或转换文件的替代方法(我的第二次尝试使用不同类型的循环,对所有文件都成功)。


编辑:我无意中发现管道进入yes |似乎ffmpeg可以解决这个问题。我添加了这个,这样我就不会被提示覆盖现有.wav文件。

编辑:感谢@roaima 的解释!事实证明,ffmpegread都继承了相同的标准输入句柄,因此ffmpeg可以在有机会之前消耗每行开头的字符read,从而破坏一些文件路径。这解释了为什么yes | ffmpeg ...有效,因为它提供了ffmpeg不同的标准输入句柄。原始循环可以很好地使用ffmpeg -nostdin ....看http://mywiki.wooledge.org/BashFAQ/089

答案1

除非您有文件目录树(未提及,并且未在示例数据集中显示),否则您可以使用简单的循环来处理文件

for f in *.flac
do
    w="${f%.flac}.wav"
    ffmpeg -i "$f" -sample_fmt s16 -ar 44100 "$w"
done

如果您确实有文件层次结构,您可以将其合并到find搜索中

find -type f -name '*.flac" -exec sh -c 'for f in "$@"; do w="${f%.flac}.wav"; ffmpeg -i "$f" -sample_fmt s16 -ar 44100 "$w"; done' _ {} +

为了提高效率,您可能希望跳过对任何flac已具有相应wav.分配后w=,添加[ -s "$w" ] && continue.如果你真的不想这样,那么你可以进一步优化命令,

find -type f -name '*.flac" -exec sh -c 'ffmpeg -i "$1" -sample_fmt s16 -ar 44100 "${1%.flac}.wav";' _ {} \;

对于预运行测试,请添加前缀ffmpegecho查看在没有实际执行的情况下会执行什么。 (不过,不会显示引号,因此请记住这一点。)


事实证明,问题实际上是关于ffmpeg咀嚼它应该处理的文件名。这是因为ffmpeg从读取命令标准输入,并且文件列表已作为循环的管道呈现while read …(也使用标准输入)。

解决办法:ffmpeg -nostdin …或者ffmpeg … </dev/null

相关内容