find
コマンドなどと組み合わせてパイプから受け取る入力を
引数としてコマンドを実行するxargs
ですが、
GNU版とBSD版の間で違いが結構大きい様です。
ちょっと調べたら色々あるみたいなんですが、 今回ひっかかった特にはまりやすいかな、と思う点についてメモしておきます。
出力が空の場合に一回実行するかどうか
簡単な例として、echo
で出力を作ってそれをそのままecho
で受け流す様なxargs
の使い方を考えます。
$ echo aaa| xargs echo "bbb"
bbb aaa
こんな感じ。パイプの後ろは
$ echo bbb aaa
に置き換わって実行されています。 これはGNUでもBSDでも同じように実行されます。
次に最初の出力を空にしてみます。
MacなどのBSD系では
$ echo | xargs echo "bbb"
$
と何も表示されません。
一方GNU版だと
$ echo | xargs echo "aaa"
aaa
$
と、echo
が実行されます。
これは例えばある特定のディレクトリにある特定の名前のファイルを表示したい時、
$ find /path/to/dir/ -maxdepth 1 -mindepth 1 -type f -name "*wanted*" -print0 2>/dev/null|xargs -0 -n1 basename
こんなコマンドをやるとします。
もし何も見つからない場合、BSD版では何も表示されない状態で正常終了します。
$ find /path/to/dir/ -maxdepth 1 -mindepth 1 -type f -name "*wanted*" -print0 2>/dev/null|xargs -0 -n1 basename
$ echo $?
0
$
一方GNU版だと
$ find /path/to/dir/ -maxdepth 1 -mindepth 1 -type f -name "*wanted*" -print0 2>/dev/null|xargs -0 -n1 basename
basename: missing operand
Try `basename --help' for more information.
$ echo $?
123
$
な感じでbasename
が引数なしで一回実行されてしまうのでエラー終了になります。
–no-run-if-empty (-r) オプション
スクリプトを使いまわしたい場合にこれだとちょっと面倒ですが、
GNU版には--no-run-if-empty
というオプションがあり
これを与えるとBSDの様に何もパイプから受け取らない場合にはコマンドを実行しない様になります。
-r
というオプションも同様の動作をします。
一方、BSD版の一部のものだと、この-r
を擬似オプション(何も変更しないオプション)として導入し、
BSDでもGNUでもxargs -r
とすれば同様の動作をする様に出来る事ができます。
ただ、Macに入ってるxargs
などは-r
オプションが無いので使うとエラーが出ます。
なのでちゃんと調べて上げる必要があって、
1 2 3 4 5 6 7 8 |
|
みたいに-r
が使えるかどうかチェックしたりしてオプションをセットします。
追記: 2017/06/13
-v
(変数が定義済みチェック)はBash 4.1などちょっと前のBashだと使えない事があるので
1 2 3 4 5 6 7 8 |
|
みたいな感じで-v
を使わずに未定義化どうかチェックした方が良いです。
上でやっているのは
- まず
-z
で未定義か空であることをチェック。 - そうであった場合、
xargs
が未定義かどうかをチェック。
${var-X}
はvar
が未定義である場合に限りX
を返します。
上の場合ではxargs_opt
が未定義であるとA
を返すので結果が正になります。
後者だけのチェックだと、もしxargs_opt
がもともとA
という値だった場合にも
間違えて正になってしまうので
最初に未定義か空のチェックでA
で無いことを確認し
その後で未定義かどうかのチェックをしています。
${var:-X}
の様に:
が入ると未定義もしくは空文字の場合にX
が返る様になります。
(通常のシェルスクリプトではこちらの方がよく使います。)
追記ここまで
オプションを扱うラッパー関数
xargs
を沢山使うような場合は
1 2 3 4 5 6 7 8 9 10 11 |
|
追記: 2017/06/13
上に書いたように最初の分岐を-z...
を使った方法に変更。
追記ここまで
みたいなラッパー関数を作ってしまって回すと便利です。