.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 的解释!事实证明,ffmpeg
和read
都继承了相同的标准输入句柄,因此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";' _ {} \;
对于预运行测试,请添加前缀ffmpeg
以echo
查看在没有实际执行的情况下会执行什么。 (不过,不会显示引号,因此请记住这一点。)
事实证明,问题实际上是关于ffmpeg
咀嚼它应该处理的文件名。这是因为ffmpeg
从读取命令标准输入,并且文件列表已作为循环的管道呈现while read …
(也使用标准输入)。
解决办法:ffmpeg -nostdin …
或者ffmpeg … </dev/null
看