xargs(1)に空の入力を与えたとき、BSD系だと何も実行されないが、Linux(というかGNU)やSolarisなど、BSD系以外だと引数なしでコマンドが実行される。(ただし、GNU xargs--no-run-if-empty/-rで抑制できる)

 SUSv3においても:

The utility named by utility shall be executed one or more times until the end-of-file is reached or the logical end-of file string is found. The results are unspecified if the utility named by utility attempts to read from its standard input.

(下線は筆者)と規定されている。ここでの”shall”は”must”と同義だ。この仕様は実用において煩わしく、論理において美しからぬものだと思うのだが、どうだろう。引数を任意個受け取るコマンドに0個の引数を渡して有意な結果が得られる場合というのがあまり想像できない。たいがいは、以下のいずれかの挙動を示し、つまらない結果になるだろう。

  • 引数が足りずエラーになる
  • デフォルトのものを対象とする(標準入力から読む、カレントディレクトリを対象とする、など)

 よくあるgrep(1)の場合は、xargs grep baa /dev/nullのような定石があって、イテレーションの最後が1引数のみの時の対策が入力が空のときの対策を兼ねてくれる。意味が二重になっているというのは一石二鳥と言える反面、美しくないとも言える。

 引数が複数のときだけファイル名を出力する仕様をフラグで制御できればいいが、残念ながらそうしたものはSUSv3には規定されていない。

 grepはまだましな方で、「find . -type f -name '*.sh' | xargs -I @ cp -p @ /some/where/」のような場合が厄介だ。cp(1)がコピーに失敗したときの処理に加え、引数がないときの文法エラーまで面倒を見なければならない。

 SUSv3を引き合いに出したので省いたが、find(1)-print0xargs(1)-0は必ずセットで付けています。

 こうして見ると、シンプルなツールといえども、というかシンプルなツールほど、妥当な仕様を決めることは難しいということが言えそうだ。色々なものと組み合わせて使われるため、最初にすべてを見通すことはできない。多くの場合に便利そうだと思って実装した親切も、後で思わぬ落とし穴になったりする。いずれ突き詰めて再考察してみたい。


Categories : Tech