Life is very short

2014-07-02

GNU版, BSD版 xargsの挙動の違い

| 06:31

細いところを見ていくと膨大な違いがあるんでしょうけど,

ハマりやすそうなところだけ.


GNU版は Linuxディストリビューションインストールされているもので,

BSD版は MacOSX, 各種 BSDディストリビューションにインストールされて

いるものとしています.


入力に非空白文字が含まれない場合の挙動

GNU版は最低 1回はコマンドが実行されますが, BSD版は入力に非空白文字が

なければコマンドが実行されません.


例えば以下のコマンドを実行したとき,

% perl -wl -e 'print "\n" for 1..100 | xargs ls'

GNU版では lsが 1回実行されますが, BSD版では何も出力されません.


--no-run-if-empty, -rオプション

GNU版 xargsには, --no-run-if-empty(-r)オプションがあり, これを

指定することで, BSD版と同じ挙動となります. FreeBSD, NetBSD, OpenBSD

xargsでは dummyオプションとして, -rを実装しているのですが, Mac

xargsでは -rオプションを実装していないので, -rをつけておけばポータブルに

なるというわけではありません.


なので, 各種 OSで動かす場合以下のようなことが必要に

なるかと思います.

#!/bin/sh

set -e

OS=$(uname)
EMPTY_OPTION=
if [ "x$OS" = "xDarwin" ]; then
    EMPTY_OPTION=''
else
    EMPTY_OPTION='-r'
fi

command1 | xargs $EMPTY_OPTION command2

STDINの /dev/ttyとして再オープン

BSD版 xargsの '-o'オプションなのですが, GNU版にはそのオプションは

実装されていません.


問題となる場面
 % echo -n somehost | xargs ssh

とすると

Pseudo-terminal will not be allocated because stdin is not a terminal.

のようなエラーが出て sshできません. このような場合 BSD版では

 % echo -n somehost | xargs -o ssh

とすると STDINの再オープンが行われて sshが問題なく行えるように

なります.


GNU版での実現

若干長くなるのですが, 以下で実現できます.

 % echo -n somehost | xargs sh -c 'ssh "$@" </dev/tty' ssh # 最後の sshは何でもよい

BSD版でもこれで動きますので, ポータブルにしたい場合は

こちらの書き方の方が良いかと思います.


おわりに

ポータビリティを保つ場合, xargsは注意して利用する必要がある

ことがわかりました.

トラックバック - http://d.hatena.ne.jp/syohex/20140702/1404250291
おとなり日記