web-dev-qa-db-ja.com

UNIX findを使用したforループ

大規模な音楽コレクションで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がこれを修正すると思いましたか?

5
wjdp

これが、neverforループを使用して、出力にスペースを含めることができるコマンドを反復処理する理由の1つです。特にその出力が、/\0を除くanythingを含むことができるファイル名のリストである場合。 bash pitfall number 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を引用したことに注意してください。これにより、findに渡す前にbashが展開しないことが保証されます。これは、現在のディレクトリにそのグロブに一致するファイルがある場合に重要です。

  • おそらくご存じのとおり、-print0を使用すると、findによって結果が改行ではなく\0で区切られます。

  • IFS=:これにより、入力フィールドの文字が何も設定されず、Wordの分割が行われなくなります。

  • while read -r -d '' file:これは結果を反復し、for file in $(command)と同様にそれぞれを$fileとして保存します。オプションは(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 '')に設定すると、readがfindの-print0でNiceを再生します。

  • "${file%.mp3}.ogg";:これは、単に.m4aサフィックスを削除し、.oggに置き換えることで、foo.oggの代わりにfoo.m4a.oggを取得します。

残りはあなたが試みたものと同じなので、あなたはそれを理解していると思います。

15
terdon

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.m4ax.m4a.oggに変換されることに注意してください。

8
muru

これはあなたが望むものの解決策かもしれません

#!/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
2
g_p