使用unix查找的for循环

wjd*_*jdp 6 command-line scripts find

尝试在大型音乐收藏中进行从 M4A 到 OGG 的大规模转换,我有:

#!/bin/sh
for i in `find /home/family/Music -name *.m4a -print0`
   #do ffmpeg -i "$i" -acodec libvorbis -aq 6 -vn -ac 2 "$i.ogg";
   do echo $i
done
Run Code Online (Sandbox Code Playgroud)

所有文件的名称中都会有空格,上面的输出显示了一个像这样的文件:

/home/family/Music/The
Kooks/Inside
In
_
Inside
Out/06
You
Don't
Love
Me.m4a
Run Code Online (Sandbox Code Playgroud)

每个空格都标记一个新行,我认为-print0会解决这个问题吗?

ter*_*don 15

这就是为什么您从不使用for循环来迭代输出可以包含空格的命令的原因之一。特别是如果该输出是一个文件名列表,其中可以包含除and之外的任何内容。您已陷入bash 陷阱 1。总是使用代替。要确保它适用于所有文件名,包括带有空格、换行符、制表符、反斜杠或任何其他奇怪字符的文件名,请使用以下命令:/\0while

find /home/family/Music -name '*.m4a' -print0 | while IFS= read -r -d '' file; do
     ffmpeg -i "$file" -acodec libvorbis -aq 6 -vn -ac 2 "${file%.m4a}.ogg";
done
Run Code Online (Sandbox Code Playgroud)

解释

  • 请注意,我引用*.mp4a了确保 bash 在将其传递给find. 这对于在当前目录中有与该 glob 匹配的文件的情况很重要。

  • -print0,正如你可能知道,导致find与分离的结果\0,而不是换行。

  • IFS= :这将输入字段字符设置为空,确保不会发生分词。

  • while read -r -d '' file:这将迭代结果,将每个保存为$file,就像for file in $(command). 选项是(来自help read):

     -r     do not allow backslashes to escape any characters
     -d delim   continue until the first character of DELIM is read, rather
        than newline
    
    Run Code Online (Sandbox Code Playgroud)

    将分隔符设置为空字符串 ( -d '') 可以read很好地与 find 的-print0.

  • "${file%.mp3}.ogg";:这简直是去除.m4a后缀,取而代之的是.ogg让您得到foo.ogg的不是foo.m4a.ogg

其余的与您尝试的相同,所以我猜您理解它。


mur*_*uru 8

使用xargswith-0选项,或使用find自己的exec选项:

find /home/family/Music -name '*.m4a' -exec ffmpeg -i "{}" -acodec libvorbis -aq 6 -vn -ac 2 "{}.ogg" \;
# or:
find /home/family/Music -name '*.m4a' -print0 | xargs -0 -i ffmpeg -i {} -acodec libvorbis -aq 6 -vn -ac 2 {}.ogg
Run Code Online (Sandbox Code Playgroud)

请注意,在这两种情况下(以及在您的原始命令中),x.m4a都将转换为x.m4a.ogg.