Linux と Mac で xargs の挙動が違うのは以前から認識していたが、詳細までは調べていなかった。今回、プライベートの git レポジトリをコミットして Mac で xargs を実行した際に illegail option -- i と怒られてしまったので、これを機会に xargs が Linux と Mac 両方できちんと動作するように修正した。
Linux では動く、Mac では動かない
# da = dot apply
alias da="ls -A ~/git/$repo/t/dot/ | xargs -i cp {} $HOME && pushd $HOME && source $HOME/.bash_profile && popd"
Mac 版の xargs (BSD) では -i オプションがサポートされていないのが問題だった。
$ da
xargs: illegal option -- i
usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements]] [-J replstr]
[-L number] [-n number [-x]] [-P maxprocs] [-s size]
[utility [argument ...]]
xargs 調査
Linux 版の xargs の man を読むと、そもそも -i オプションはすでに deprecated となっており -I オプションを使うことが推奨されている。単純に -i を -I に変えればいいのかと思ったら、少し事情は異なっていて -I オプションは -i と違ってデフォルトの replace-str に {} を設定してくれないので、明示的に {} を指定する必要がある。
$ man xargs
(.. snip ...)
-I replace-str
Replace occurrences of replace-str in the initial-arguments with names read from standard input. Also, unquoted blanks do not terminate input items;
instead the separator is the newline character. Implies -x and -L 1.
-i[replace-str], --replace[=replace-str]
This option is a synonym for -Ireplace-str if replace-str is specified. If the replace-str argument is missing, the effect is the same as -I{}. This
option is deprecated; use -I instead.
(.. snip ...)
上記を踏まえて以下のように修正した。(๑•̀ㅂ•́)و✧
Linux でも Mac でも動く(replace-str に伝統的(?) に {} を使用)
$ git diff -U0 HEAD~2 HEAD
(... snip ...)
-alias da="ls -A ~/git/$repo/t/dot/ | xargs -i cp {} $HOME && pushd $HOME && source $HOME/.bash_profile && popd"
+alias da="ls -A ~/git/$repo/t/dot/ | xargs -I {} cp {} $HOME && pushd $HOME && source $HOME/.bash_profile && popd"
(.. snip ...)
Linux でも Mac でも動く(replace-str に独自のトークンを使ってみた例)
$ git diff -U0 HEAD~2 HEAD~1
(.. snip ...)
-alias da="ls -A ~/git/$repo/t/dot/ | xargs -i cp {} $HOME && pushd $HOME && source $HOME/.bash_profile && popd"
+alias da="ls -A ~/git/$repo/t/dot/ | xargs -I _file_ cp _file_ $HOME && pushd $HOME && source $HOME/.bash_profile && popd"
(... snip ...)