xargsで複数のパラメータに対してプレースホルダを使う

環境

xargsで -n と -I が併用できない問題

xargs を使う際、-I オプションを使ってパラメータをプレースホルダに差し込むというのをよくやります。

# テスト用ファイル
$ cat data.txt 
aaaa
bbbb
cccc
dddd

# ファイルの各行に対して xargs でコマンド実行
# -I で指定したプレースホルダ @ の場所にパラメータが正しく置換されている
$ cat data.txt | xargs -I@ echo "The input is [@]."
The input is [aaaa].
The input is [bbbb].
The input is [cccc].
The input is [dddd].

パラメータが1つの場合はこれでいいんですが、-n オプションで2つ以上のパラメータを使おうとすると -I が使えないため、すごく困ります。

# xargs -n2 でパラメータ2つずつに対してコマンド実行
# 先に指定した -I@ が無視されて -n2 だけが有効になり、パラメータの置換が行われない
$ cat data.txt | xargs -I@ -n2 echo "The inputs are [@]."
xargs: warning: options --replace and --max-args/-n are mutually exclusive, ignoring previous --replace value
The inputs are [@]. aaaa bbbb
The inputs are [@]. cccc dddd

sh -c 経由で実行して回避する

これは xargs に渡すコマンドを sh -c 経由で実行し、シェルの引数 $1, $2, ... を使うことで代替できます。

$ cat data.txt | xargs -n2 sh -c 'echo "The inputs are [$1, $2]."' sh
The inputs are [aaaa, bbbb].
The inputs are [cccc, dddd].

xargs から sh -c 'echo "The inputs are [$1, $2]."' sh aaaa bbbb のような形で sh にパラメータが渡されるので、あとは sh で実行するコマンドの中で $1, $2 等を好きなように使えばOK、という具合です。

コマンド末尾の sh は、 $0 として渡されるコマンド名を明示的に指定しているだけです。 今回の場合であればこれを無くして xargs -n2 sh -c 'echo "The inputs are [$0, $1]."' のようにしてもよいのですが、例えばコマンド内で $@ を使った場合に $0 は除外されるといった扱いの違いもあるので、指定しておくとちょっとだけ安心です。

参考