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 ...)