使用 unix find 进行循环

使用 unix 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

所有文件的名称中都会有空格,上面的输出显示单个文件如下:

/home/family/Music/The
Kooks/Inside
In
_
Inside
Out/06
You
Don't
Love
Me.m4a

每个空格都标记一个新行,我想-print0可以解决这个问题吗?

答案1

这就是你绝不使用for循环来迭代输出可能包含空格的命令。特别是如果该输出是可能包含以下内容的文件名列表任何事物除了/\0。你已经陷入bash 陷阱 1。始终使用while。为确保它适用于所有文件名,包括带有空格、换行符、制表符、反斜杠或任何其他奇怪字符的文件名,请使用以下命令:

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

解释

  • 请注意,我引用了*.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
    

    将分隔符设置为空字符串 ( -d '') 可以使readfind 更好地发挥作用-print0

  • "${file%.mp3}.ogg";:这只是删除.m4a后缀并将其替换为,.ogg这样您就得到了foo.ogg而不是foo.m4a.ogg

其余部分与您尝试过的相同,所以我想您理解它。

答案2

使用xargs使用-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

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

答案3

这可能是你想要的解决方案

#!/bin/bash    
find  /home/family/Music -type f -name '*.m4a' -print0 | while IFS= read -r -d '' i; 
  do
   #do ffmpeg -i "$i" -acodec libvorbis -aq 6 -vn -ac 2 "$i.ogg"; 
   echo $i
done

相关内容