Posted at

Linux と FreeBSD, macOS でコマンドが異なる問題にどう対処するか

More than 1 year has passed since last update.

Linux (GNU coreutils) と FreeBSD, macOS でコマンドが違うことにより、シェルスクリプトにおいてスクリプトが実行される環境を考慮する必要がある。例えばファイルの最終更新日時を取得したいとして stat を利用したいが、stat の出力フォーマットを指定するフラグは GNU coreutils と FreeBSD, macOS で異なる。

前提として Perl や Python で書けというのもその通りだと思うが、bash や zsh のプラグインなどどうしてもシェルスクリプトでないとダメなことはある。自分で書いて自分で使うだけなら些細な問題だが、他人に共有されるとなるとそうもいかない。


TL;DR


  • シンタックスの違いを利用する (stat)

  • 同一のシンタックスを持つコマンドで代用する (openssl)


方法その1 uname によって環境を判定する

もっともよくある対処方法として uname による判定がある。

case "$(uname)"; in

Linux)
# GNU coreutils
;;
Darwin)
# macOS
;;
esac

これはひとつ大きな問題があって、macOS で GNU coreutils を使っているとそもそも環境による判定は失敗してしまう。まさに自分も自分が使うシェルスクリプトは GNU coreutils 前提にしたくて使ってしまっている。以上により、環境によって判定するのはあまりよろしくない。


方法その2 シンタックスの違いを利用する

例えば stat であれば --help フラグは GNU coreutils にしか存在しないため、これを実行したときの終了コードによって判定できる。

if stat --help >/dev/null 2>&1; then

modified_time_fmt="-c%y" # GNU coreutils
else
modified_time_fmt="-f%m" # FreeBSD, macOS
fi

stat $modified_date_fmt /path/to/file # It works!

これは環境によって判定しないため macOS で GNU coreutils を使っているなどの場合においてもうまくいくのだが、そもそも判定せずに使えるのならそれがベストである。


方法その3 同一のシンタックスを持つコマンドで代用する

md5 を取得したい。GNU coreutils では md5sum が使えるが、macOS, FreeBSD にこのコマンドは存在せず md5 を使う必要がある。

この場合 openssl の利用を選択できる。openssl は OpenSSL のコマンドラインユーティリティなのでバージョンによる違いはあれど環境による違いはない。

$ openssl md5 file

MD5(file)= d41d8cd98f00b204e9800998ecf8427e

この方法は openssl がインストールされていることを前提としているため、他の方法とは利用できる条件が異なっているが、ひとつの選択肢として考慮できる。


まとめ

シェルスクリプトは実行環境の違いによる問題があるため、利用する場合も小さいものに留めたほうがよい。