環境
- Ubuntu 22.04
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 は除外されるといった扱いの違いもあるので、指定しておくとちょっとだけ安心です。