【find】複数ファイルをまとめてafconvertする【xargs】
こんばんは.1000chです. 翌日午前中から予定があると,夜中に無理出来なくて進捗がアレですね. ライフサイクルをずらせ,という話でしょうか.
さて,今回はunixコマンドネタです.
いきますよー.
問題
今回ぶつかった問題はこんな感じです.
「ディレクトリ内のxxx.mp3を全て,xxx.wavに変換したい!」
iTunesでもなんでもつかえばいいって?そーゆーことじゃねーんだよ
実はMacには,ターミナル上で音声データを変換するコマンドが最初から入っています. それがafconvertというコマンドです. このコマンド,wavやらmp3やらaiffやらの音声ファイルを双方向に変換できる便利ツールなんです. (が,オプションの指定が結構複雑で,細かいこと考えると使い方難しいかも)
細かい説明は他にゆずって,mp3->wavの場合はこんな感じでコマンド叩きます.
$ afconvert -f WAVE -d LEI16 <input>.mp3 (<output>.wav) #output名はoptional
具体的にはこんな感じ.
1000ch$ ls 1-03 Snow halation.mp3 1000ch$ afconvert -f WAVE -d LEI16@44100 1-03\ Snow\ halation.mp3 snow.wav 1000ch$ ls -1 1-03 Snow halation.mp3 snow.wav
スノハレ,良い曲ですね.
afconvert
,1ファイルだけなら楽なんですが,複数ファイルが対象になるとちょっと面倒です.
$ ls -1 12 START_ DASH!! (ELI Mix).mp3 12 START_ DASH!! (NOZOMI Mix).mp3 13 START_ DASH!! (MAKI Mix).mp3 14 START_ DASH!! (HONOKA Mix).mp3 14 START_ DASH!! (KOTORI Mix).mp3 14 START_ DASH!! (UMI Mix).mp3 15 START_ DASH!! (HANAYO Mix).mp3 15 START_ DASH!! (NICO Mix).mp3 15 START_ DASH!! (RIN Mix).mp3 # まとめて処理したいなー $ afconvert -f WAVE -d LEI16 *.mp3 Audio File Convert Version: 2.0 Copyright 2003-2013, Apple Inc. All Rights Reserved. Specify -h (-help) for command options Usage: afconvert [option...] input_file [output_file] Options may appear before or after the direct arguments. If output_file is not specified, a name is generated programmatically and the file is written into the same directory as input_file. afconvert input_file [-o output_file [option...]]... Output file options apply to the previous output_file. Other options may appear anywhere. Help options: { -hf | --help-formats } print a list of supported file/data formats { -h | --help } print help # 使い方が間違っているので利用方法のメッセージが出てきた # え,じゃー9回分afconvert打つの?だる..
スタダも良い曲ですね.
こーゆーのはafconvertに限った話ではなく,複数ファイルに同一の処理を行いたいことってよくありますよね. シェルスクリプト書けばできるけど,そこまでする?みたいなやつ.
てことで,「複数ファイルに処理を行う」方法を調べました.
find + xargs
ちょいとggって出てきたのがこちら.
find/xargsを使ったファイル・ディレクトリ名の一括置換/一括作成コマンド一覧
どうやらfind
コマンドとxargs
コマンドを組み合わせると,逐次処理っぽいのができるらしい.
xargs
を初めて知ったので使い方を調べます.
Linuxコマンド集 - 【xargs】標準入力から生成したコマンドラインを実行する:ITpro
要するに,
標準出力を受けてコマンドを生成,実行するコマンドのようです. てきとーに出力をパイプで与えてあげればいいみたい.
試しにやってみます.
1000ch$ ls bar.txt hoge.txt huga.txt log piyo.csv tmp # lsした結果をxargsに渡す.デフォルトではechoが実行される 1000ch$ ls | xargs bar.txt hoge.txt huga.txt log piyo.csv tmp # これと同値 1000ch$ ls | xargs echo bar.txt hoge.txt huga.txt log piyo.csv tmp # ls -aした結果を(ry 1000ch$ ls -a | xargs . .. bar.txt hoge.txt huga.txt log piyo.csv tmp # ls-lした(ry 1000ch$ ls -l | xargs total 8 -rw-r--r-- 1 1000ch staff 0 11 11 11:29 bar.txt -rw-r--r-- 1 1000ch staff 0 11 11 11:29 hoge.txt -rw-r--r-- 1 1000ch staff 0 11 11 11:29 huga.txt -rw-r--r-- 1 1000ch staff 21 11 11 11:52 log -rw-r--r-- 1 1000ch staff 0 11 11 11:29 piyo.csv drwxr-xr-x 5 1000ch staff 170 11 11 12:52 tmp
基本的に,パイプして与えた文字列をひとかたまりで処理してる感じですね.
xargsで実行するコマンドに,明示的に引数を与える場合は-I
を使うみたい.(Macの場合)
# -Iオプションで置換文字列を指定 1000ch$ ls | xargs -I{} echo {} bar.txt hoge.txt huga.txt log piyo.csv tmp # コレはダメ 1000ch$ ls | xargs -I echo {} xargs: {}: No such file or directory # コレもダメ 1000ch$ ls | xargs echo {} {} bar.txt hoge.txt huga.txt log piyo.csv tmp # 置換文字列は自由に指定可能 1000ch$ ls | xargs -I% echo % bar.txt hoge.txt huga.txt log piyo.csv tmp
これだけで先のafconvert
は上手く出来そうな感じがします.
左側をlsから,find . -name '*.mp3'
とかにすれば良さそう.
1000ch$ ls -1 12 START_ DASH!! (ELI Mix).mp3 12 START_ DASH!! (NOZOMI Mix).mp3 13 START_ DASH!! (MAKI Mix).mp3 14 START_ DASH!! (HONOKA Mix).mp3 14 START_ DASH!! (KOTORI Mix).mp3 14 START_ DASH!! (UMI Mix).mp3 15 START_ DASH!! (HANAYO Mix).mp3 15 START_ DASH!! (NICO Mix).mp3 15 START_ DASH!! (RIN Mix).mp3 # カレントディレクトリ内のmp3ファイルをwaveに変換 1000ch$ find . -name "*.mp3" | xargs -I % afconvert -f WAVE -d LEI16@44100 % 1000ch$ ls -1 12 START_ DASH!! (ELI Mix).mp3 12 START_ DASH!! (ELI Mix).wav 12 START_ DASH!! (NOZOMI Mix).mp3 12 START_ DASH!! (NOZOMI Mix).wav 13 START_ DASH!! (MAKI Mix).mp3 13 START_ DASH!! (MAKI Mix).wav 14 START_ DASH!! (HONOKA Mix).mp3 14 START_ DASH!! (HONOKA Mix).wav 14 START_ DASH!! (KOTORI Mix).mp3 14 START_ DASH!! (KOTORI Mix).wav 14 START_ DASH!! (UMI Mix).mp3 14 START_ DASH!! (UMI Mix).wav 15 START_ DASH!! (HANAYO Mix).mp3 15 START_ DASH!! (HANAYO Mix).wav 15 START_ DASH!! (NICO Mix).mp3 15 START_ DASH!! (NICO Mix).wav 15 START_ DASH!! (RIN Mix).mp3 15 START_ DASH!! (RIN Mix).wav
やったぜ(`・ω・´)
なお,xargs
ではデフォルトで空白を区切り文字と見なしているようで,このままだと空白を含むファイルを扱う際に思うような挙動にならないことがあります.
この問題を解決するには,区切り文字を明示的に変更する必要があります.
やることは簡単で,find
側では-print0
オプションを,xargs
側では-0
オプションをつけるだけ.
(..らしいんだけど,手元だとなぜか空白はokで,アポストロフィがNGだった.Mac版の仕様なのでしょうか)
1000ch$ tree . ├── hello\ world │ └── hello\ world.txt ├── my\ name\ is\ 1000ch.txt └── what's\ your\ name └── 1000ch.txt 2 directories, 3 files 1000ch$ find . -name "*.txt" | xargs -I % echo % ./hello world/hello world.txt # 空白含んでいても出力ok ./my name is 1000ch.txt xargs: unterminated quote #クオートを含む名前がNG 1000ch$ find . -name "*.txt" -print0| xargs -0 -I % echo % ./hello world/hello world.txt ./my name is 1000ch.txt ./what's your name/1000ch.txt #クオート含む名前も出力ok
更に,xargs
への標準出力は間にsed
噛ませてちょこっと変更させたりも出来ます.
応用すると全ての.txtファイルを.mdに変換なんてこともできたり.
1000ch$ ls -1 ts1.txt ts2.txt ts3.txt # 拡張子.txtのファイルを探して # .txtを外した文字列strを取得して # str.txtをstr.mdにcp 1000ch$ find . -name "*.txt" | sed -e "s/.txt//" | xargs -I % cp %.txt %.md 1000ch$ ls -1 ts1.md ts1.txt ts2.md ts2.txt ts3.md ts3.txt
find + sed + xargs の使い方をおぼえておくと,いざという時に役に立つかも?
後日談
実はfind
には-exec
っていうオプションがあって,ヒットしたファイルに逐次コマンドを実行することが出来るらしい.
というわけで,単にディレクトリ内の全ファイルをafconvertするだけならこれでok.
$ find . -name "*.mp3" -exec afconvert -f WAVE -d LEI16 {} \;
xargs
とか使う必要なかったですね.
ちなみに,find -exec
とxargs
の違いは,xargs
においてはfindの結果を一つの固まりとして扱うことが出来ることだそうです.
ふむ...どんな利点があるのやら.
まぁ,xargs
使った方がコマンド何したいのか,はわかりやすいんじゃないでしょうか.
どうせワンライナーなんで,好きなようにやれば良いと思います←
まとめ
afconvert
を一気にやりたいfind
+xargs
便利find
+sed
+xargs
も便利find -exec
で事足りるっちゃ事足りる