ハイパーマッスルエンジニア

Vim、ShellScriptについてよく書く

whichコマンドでaliasではなくPATHを表示する(where, whence, where, type, commandどれ使えばいいのか決める)

f:id:rasukarusan:20200217024852p:plain:w300

コマンドにaliasを貼っていると、whichコマンドでPATHを知りたいのにaliasが表示されてしまう。

$ which grep
grep: aliased to grep --color=auto

PATHを知りたいときは-pをつけるとPATHが表示できる。(※-pはzshのみ有効)

$ which -p grep
/usr/local/opt/grep/libexec/gnubin/grep

ちなみに-aをつけるとaliasとPATHが両方表示される。(複数存在する場合、全部表示される)

$ which -a grep
grep: aliased to grep --color=auto
/usr/local/opt/grep/libexec/gnubin/grep
/usr/bin/grep

コマンドのPATHを知るいくつかの方法

PATHを知ることができるのはwhichコマンドの他にもいくつかある。

  • where(zshのみ)
$ where grep
grep: aliased to grep --color=auto
/usr/local/opt/grep/libexec/gnubin/grep
/usr/bin/grep
  • whereis
$ whereis grep
/usr/bin/grep
  • type
$ type -a grep
grep is an alias for grep --color=auto
grep is /usr/local/opt/grep/libexec/gnubin/grep
grep is /usr/bin/grep
  • command
$ command -vp grep
/usr/bin/grep
  • whence(zshのみ)
$ whence -a grep
grep --color=auto
/usr/local/opt/grep/libexec/gnubin/grep
/usr/bin/grep

で、結局どれを使えばいいの?

terminalでちょろっと打つぐらいなら自分が覚えてるやつを打てばいいと思う。

だが、ShellScriptにして配布等するとき、つまり汎用性を高くしたいならちょっと考えたほうがいい。
ShellScript内でよく使われる記法としては、下記のようにコマンドがインストールされているかを判定するものが挙げられる。

# lsが使用可能か判定
if which ls >/dev/null 2>&1; then
    echo 'Found!'
else
    echo 'Not Found!'
fi

このような使い方をする場合、とりえあずwhichwhencewhereは避けたほうがいい。
whichはギリセーフ感はあるが、-pがzshにしか無いのでできれば避けたい。また、ビルトインコマンドではなく外部コマンドなのでちょっと遅い。判定のために外部プロセスを使うのはちょっとリッチすぎるかなっていう感じ。
whencewhereはzshでしか動かないので却下。

となるとwhereistypecommandになるが、whereisも脱落してもらう。
なぜならwhereisはGNU系ではコマンドだけではなく、 バイナリ, ソース, マニュアルページのファイルを探すためにも使用されるコマンドのため、思考停止でshellを読むことが難しくなる(まあパッと見たらわかると思うけど、読み手の労力は極力減らすべき)。

さて、残るはtypecommandだけだが、個人的にcommandはあんまり使わない。 commandはパスを知りたい場合-vpをつける必要があり、仮につけなかった場合にエラーとなってしまう。(正確には「引数で指定したコマンドを実行する」という挙動になってしまう)

# -vpをつける必要がある
$ command -vp grep
/usr/bin/grep

# エラーになる(シンプルにgrepが実行されてしまう)
$ command grep
Usage: grep [OPTION]... PATTERNS [FILE]...
Try 'grep --help' for more information.

whereisと同じでcommandはパスを知る以外にも用途が存在するため、あまり使いたくない。
(が、確かcommandはPOSIX互換性があるとかあった気がするし、typeと違って終了ステータスが明確になっているので、全然悪い選択肢ではなく、むしろ良い。)

以上から、汎用性も高く、書きやすく、読み手にも何をしたいのか一撃でわかるという理由からtypeを使用するべき。もしくはcommand -vp

つまりコマンドの存在を調べるshellはこれ

コマンドの存在を何回もチェックするような場合、下のような関数を作ることが多い。

# jsっぽい関数名でhas()とかでもいいかもね
exists() {
    type "$1" >/dev/null 2>&1
}

# 使用するとき
if exists ls; then
    echo 'Found!'
else
    echo 'Not Found!'
fi

# もしくは一行で
exists ls && echo 'Found!' || echo 'Not Found!'

終わり

意外に奥深いよね。 (あとhashコマンドもあった。まあいいか。)