xargsメモ

xargsはよくfindと組み合わせて一括処理したりするのに使うけど、失敗すると大ダメージを受けるのでお勉強。

プログラムを指定しないとecho

xargsにプログラムを指定しないとechoが使われるのでどんな感じに渡されるのか確認できる。

$ ls -l
total 0
-rw-r--r--  1 pasela  staff     0B  1 22 17:11 foo
-rw-r--r--  1 pasela  staff     0B  1 22 17:11 bar
-rw-r--r--  1 pasela  staff     0B  1 22 17:11 baz

$ ls | xargs
foo bar baz

NUL(\0)区切りで受ける

xargsはデフォルトではホワイトスペースで区切るので、たとえば"Foo Bar"というような空白を含むファイルがあるときに普通に実行すると残念なことになる。

$ ls -l
total 0
-rw-r--r--  1 pasela  staff     0B  1 22 17:11 Foo Bar
-rw-r--r--  1 pasela  staff     0B  1 22 17:11 baz

$ find . type -f -print | xargs rm
rm: ./Foo: No such file or directory
rm: Bar: No such file or directory

これはこんな感じの3引数として渡される。

rm "./Foo" "Bar" "baz"

これを避けるにはリストを\0区切りで出力して、受ける方も\0区切りで受けるとよい。
findのほうは-print0を使うと\0区切りで出力してくれ、xargsは-0で入力を\0区切りとして受け取れる。

$ find . type -f -print0 | xargs -0 rm

これはこんな感じの2引数として渡される。

rm "./Foo Bar" "baz"


ちなみにGNU版の場合は-d delimで他にも任意のデリミタを指定することができる。

実行コマンドを表示する

-tを指定すると実行するコマンドを表示してくれる。

$ ls | xargs -t
/bin/echo bar baz foo
bar baz foo

表示した上で実行もするので、dry run的な確認には使えない。勘違いしないように。
ログとして残したい場合なんかにいいかも?

実行するかどうか確認する

-pを指定するとコマンド実行毎に実行するかどうか確認してくる。yで実行、それ以外でスキップ。

$ ls | xargs -p
/bin/echo bar baz foo?...y
bar baz foo

入力n行ごとに実行する

-L 数字で指定行数ごとにコマンドを実行させられる。

$ ls | xargs -L 1 -t
/bin/echo bar
bar
/bin/echo baz
baz
/bin/echo foo
foo

$ ls | xargs -L 2 -t
/bin/echo bar baz
bar baz
/bin/echo foo
foo

引数n個ごとに実行する

-n 数字で指定個数の引数ごとにコマンドを実行させられる。

$ ls | xargs -n 1 -t
/bin/echo bar
bar
/bin/echo baz
baz
/bin/echo foo
foo

$ ls | xargs -n 2 -t
/bin/echo bar baz
bar baz
/bin/echo foo
foo

ん、-Lとの違いがわからない?
ではこうやって1行の入力にしてみると違いがわかるはず。

$ echo "foo bar baz" | xargs -n 1 -t
/bin/echo foo
foo
/bin/echo bar
bar
/bin/echo baz
baz

$ echo "foo bar baz" | xargs -L 1 -t
/bin/echo foo bar baz
foo bar baz

-nのほうはホワイトスペースで3つに区切られた上で、1つずつ実行されている。
-Lのほうは1行の入力ずつ実行されている。

置換文字列で挿入する

普通はxargsに渡したコマンドの最後にxargsが引数を追加して実行されるが、cpやmvみたいにコマンドラインの途中の位置に挿入して欲しい場合は-I repstrが使える。

普通にやると……

$ ls | xargs -t cp /path/to/dest_dir
cp /path/to/dest_dir bar baz foo
usage: cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file target_file
       cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file ... target_directory

こんな感じのコマンドラインで実行されてしまうが、-Iを使うと……

$ ls | xargs -I % -t cp % /path/to/dest_dir
cp bar /path/to/dest_dir
cp baz /path/to/dest_dir
cp foo /path/to/dest_dir

このように-Iで指定した文字列のところを置換して処理してくれる。

BSDの場合)もうちょっと賢く

上記のは見てのように自動的に-L 1として処理されてしまうので効率があまりよくない。
ここはcp bar baz foo /path/to/dest_dirってなってほしいよね?
こういう場合、BSD版なら-J repstrを使うとよい。

$ ls | xargs -J % -t cp % /path/to/dest_dir
cp bar baz foo /path/to/dest_dir
GNU coreutilsの場合(2013-03-05追記)

GNU版のcpやmv等の場合、--target-directoryというdestinationを指定するオプションがあるので、-Iを使わずに次のようにすることもできる。

$ ls | xargs -t cp --target-directory=/path/to/dest_dir
cp --target-directory=/path/to/dest_dir bar baz foo

通常、引数の最後となるdestinationをオプションで手前に持ってくることができる。

複数プロセスで並列に実行する

-P 数字を使うと指定したプロセス数で並列実行してくれる。デフォルトは1。

例えばこんな感じのURLリストがあったとして……

$ cat urls.txt
http://www.google.co.jp/
http://www.yahoo.co.jp/
http://www.goo.ne.jp/

こんな感じで実行すると普通は1つずつ実行される。

$ cat urls.txt | xargs -n 1 -t curl >/dev/null
curl http://www.google.co.jp/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 12352    0 12352    0     0   130k      0 --:--:-- --:--:-- --:--:--  177k
curl http://www.yahoo.co.jp/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 25694    0 25694    0     0  53247      0 --:--:-- --:--:-- --:--:--  253k
curl http://www.goo.ne.jp/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 64743    0 64743    0     0   630k      0 --:--:-- --:--:-- --:--:--  916k

-Pを指定してやるとこれを並列に実行して一気にダウンロードできる。

$ cat urls.txt | xargs -n 1 -P 3 -t curl >/dev/null
curl http://www.google.co.jp/
curl http://www.yahoo.co.jp/
curl http://www.goo.ne.jp/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 12340    0 12340    0     0   127k      0 --:--:-- --:--:-- --:--:--  191k
100 64796    0 64796    0     0   590k      0 --:--:-- --:--:-- --:--:--  832k
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 25703    0 25703    0     0  59137      0 --:--:-- --:--:-- --:--:--  522k