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
がインストールされていることを前提としているため、他の方法とは利用できる条件が異なっているが、ひとつの選択肢として考慮できる。
まとめ
シェルスクリプトは実行環境の違いによる問題があるため、利用する場合も小さいものに留めたほうがよい。