読者です 読者をやめる 読者になる 読者になる

シェルスクリプトの中でコマンドが存在するかどうかを確かめる方法

stackoverflow に答えとともに良い説明があった。
http://stackoverflow.com/a/677212/2122085

#!/bin/sh なスクリプトの場合(bash ではなく sh を使っている場合)は

\command -v foo >/dev/null 2>&1 || { echo >&2 "'foo' is required.  Aborting."; exit 1; }

で OK。

こういう場合、よく which を使う(というか僕はいままで which を使っていた)のだけど、それだとどうもコマンドが見つからなかった時に 0(正常終了)を返してくる環境があるらしく、また which は外部コマンドなので多少オーバーヘッドがあったり、ディストリビューションによっては which をカスタマイズしてパッケージマネージャと連携させていたりするらしいのでヘビーデューティなスクリプトだとパフォーマンスに多少影響を与えることになるのかもしれない。

一応上記コマンドの解説。

\command -v は sh のビルトインで、引数で指定されたコマンド(外部コマンド、ビルトインコマンド、エイリアスを含む)を実行はせずに、外部コマンドならそのコマンドの絶対パス、ビルトインコマンドならコマンド名、エイリアスならエイリアスの展開、をそれぞれ表示してくれる。
(stackoverflow の回答では先頭に \ をつけていないけど、念のためつけておくことにした。http://bearmini.hatenablog.com/entry/2013/04/13/000055 参照。)

foo は探したいコマンド。

>/dev/null 2>&1 は command -v の出力を(エラー出力も含めて)捨てている。

|| { 〜 } の部分は、コマンドが見つからなかった時に command ビルトインの終了コードが 0 以外になるので実行される部分。上記の例ではエラーメッセージを表示して exit 1 している。



(sh ではなく)#!/bin/bash を使用している場合は command ビルトインの代わりに type もしくは hash ビルトインが使える。

hash ビルトインを使うと、コマンドをサーチしてその場所がハッシュされて残るので、実際にそのコマンドを実行するときのサーチが高速になるとのこと。ただし外部コマンドを探すときにしか使えず、ビルトインコマンドを探すことができないという欠点がある。

type ビルトインを使うと、2回目以降のサーチが速くなったりはしないがビルトインコマンドも探すことができる。command ビルトインとの違いはよくわからないけど、man を読んだ限りでは、type だと tracked alias というものにも対応しているのかな?ただ、tracked alias の説明をググったりして探してみても ksh というシェルの機能のようで bash では使えなさそう。command -v より type のほうがキータイプ数(ファイルに保存される文字数)が少なくなるというメリットがあるのかな?
上記 stackoverflow の回答では bash のときは hash か type を使えと書いてあるけど command -v ではいけない理由までは書いてないな。

なんかモヤモヤしたままこの記事は終了^^;