xargsでコマンドを並列実行


GNU parallelを使うとコマンドを並列実行でき、細かい制御ができる。使い方もそれほど難しくない。
しかし、OSに標準で入っているxargsでも簡単な制御でよければ並列処理ができる。

基本的な使い方

処理対象が100個あり、1プロセスあたり4個を引数にして、同時に3個のプロセスを起動させる例:

$ seq 100 | xargs -t -P3 -n4 ruby -e 'sleep 5'

-t     実行する前にそのコマンドラインを表示する
-P3    3プロセスを並列に実行
-n4    各プロセスが引数4個をとるようにする(入力をスペースによって引数に区切る)

-Pを指定した場合は-nまたは-Lも同時に指定すること。そうしないと1プロセスしか起動してこない。

これを実行すると、プロセスの状況は下記のようになる。

$ watch -n1 'ps aux | grep [r]uby'

ken    18746  0.7  0.2 141948  9136 pts/1    Sl+  12:57   0:00 ruby -e sleep 5 1 2 3 4
ken    18747  1.2  0.2 141924  9060 pts/1    Sl+  12:57   0:00 ruby -e sleep 5 5 6 7 8
ken    18748  1.2  0.2 141932  9000 pts/1    Sl+  12:57   0:00 ruby -e sleep 5 9 10 11 12
  • 入力から4個ずつ引数をとり、コマンドを実行する
  • 同時に3個のプロセスが起動している
  • 5秒後には各プロセスが終了し、次の
ruby -e sleep 5 13 14 15 16
ruby -e sleep 5 17 18 19 20
ruby -e sleep 5 21 22 23 24

のプロセスが起動してくる。

-nと-Lの違い

-n3    各プロセスが引数3個をとるようにする(入力をスペースによって引数に区切る)
-L3    各プロセスが引数3個をとるようにする(入力を改行によって引数に区切る)

入力にスペースが含まれている場合の挙動が違う。

$ echo one two "three four"
one two three four

$ echo one two "three four" | xargs -n1 ruby -e 'p ARGV'
["one"]
["two"]
["three"]
["four"]

$ echo one two "three four" | xargs -L1 ruby -e 'p ARGV'
["one", "two", "three", "four"]

man xargsには

Use the -n option with -P; otherwise chances are that only one exec will be done.
(-Pと同時に-nを使うこと。そうしないと1回しかexecされないだろう)

と書いてあったが、-nでなく-Lでもいいようだった。
(追記:GNU findutilsに含まれるxargs 4.6.0ではmanもそのように修正されていた)

備考

-pをつけるとコマンドを実行する前にそのコマンドラインを表示して確認を求めてくれる。

xargsもLinux(GNU)とBSD(Mac含む)では一部のオプションが異なるので注意。