コマンドの存在を調べるときはwhichよりtypeの方がいい

この記事はImaizumi Lab Advent Calendarの13日目です。

TL;DR

whichで出てくるパスの実行ファイルが実行されているとは限らない

詳しくみる

※私はfishを使っています。typeコマンドの-sオプションはないシェルもあります。

普通のコマンドの場合

$ type -s brew
brew is /usr/local/bin/brew
$ which brew
/usr/local/bin/brew

一致しますね。

ビルトインコマンドの場合

$ type -s source
source is a builtin
$ which source
(何も表示されない)

ビルトインコマンドということを知らずに実行してしまうと「コマンドがない!」と焦ることになりそうです。

typeとwhichで表示が異なる場合

$ type -s cd
cd is a function (defined in /usr/local/Cellar/fish/3.1.2/share/fish/functions/cd.fish)
$ which cd
/usr/bin/cd

typeとwhichで結果が異なっています。実際にはtypeで表示されたパスのコマンドの方が読み込まれています。

typeとwhichの結果が異なる理由

そもそもwhichとは、PATHに設定されているディレクトリを順番に調べて最初に見つかった実行ファイルを表示するコマンドです。(オプションなしの実行時)

実際に実行されるコマンドのパスとは限らないのです。

ビルトインコマンドであるcdやechoなどは、実行ファイルが/binや/usr/bin下にも存在することがあり、そういったケースでwhichを使うと実際に実行されるコマンドのパスと出てくるパスが異なってしまいます。*1

(2020/12/19追記) Macの場合ですが、/binや/usr/bin下にあるビルトインコマンドと同名のコマンドの中身はbuiltin(ビルトインコマンドを優先実行するコマンド)のようです。結局のところビルトインコマンドが実行されているようです。

困りそうなケースとしては、ビルトインコマンドと同名のコマンドを作成して/bin下に置いている時とかでしょうか。

思わぬハマりポイントになりかねないので、なるべくtypeを使った方がいいと思います。

*1:ビルトインコマンドなのでコマンドのパスという言い方が適切なのかどうかは...