実行可能ファイルへのパスを探したり、Unixシェルにコマンド名を入力した場合に何が起こるかを確認したりする場合、さまざまなユーティリティ(which)が多数あります。 、type 、command 、whence 、where 、whereis 、whatis 、hash など)。
そのwhichをよく耳にします 避けるべきです。なんで?代わりに何を使うべきですか?
承認された回答:
これが、あなたがそれについて知りたくないと思ったことのないすべてです:
概要
Bourneのようなシェルスクリプトで実行可能ファイルのパス名を取得するには(いくつかの注意点があります。以下を参照してください):
ls=$(command -v ls)
特定のコマンドが存在するかどうかを確認するには:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
インタラクティブなボーンのようなシェルのプロンプトで:
type ls
which コマンドはCシェルからの壊れた遺産であり、ボーンのようなシェルにそのままにしておく方がよいでしょう。
ユースケース
スクリプトの一部としてその情報を検索することと、シェルプロンプトでインタラクティブに検索することには違いがあります。
シェルプロンプトでの一般的な使用例は次のとおりです。このコマンドは奇妙な動作をします。正しいコマンドを使用していますか? mycmdと入力したときに正確に何が起こったのか ?それが何であるかをさらに調べることはできますか?
その場合、実際にコマンドを呼び出さずにコマンドを呼び出したときにシェルが何をするかを知りたいと思います。
シェルスクリプトでは、かなり異なる傾向があります。シェルスクリプトでは、実行するだけでコマンドがどこにあるのか、何であるのかを知りたい理由はありません。一般に、知りたいのは実行可能ファイルのパスであるため、実行可能ファイルからより多くの情報を取得できます(それに関連する別のファイルへのパスや、そのパスにある実行可能ファイルのコンテンツから情報を読み取るなど)。
インタラクティブに、すべてについて知りたい場合があります my-cmd システム上で、スクリプトで使用できるコマンドは、めったにありません。
利用可能なツールのほとんど(よくあることですが)は、インタラクティブに使用できるように設計されています。
歴史
最初に少し歴史があります。
70年代後半までの初期のUnixシェルには、関数やエイリアスがありませんでした。 $PATHでの実行可能ファイルの従来の検索のみ 。 csh 1978年頃にエイリアスが導入されました(ただし、csh 最初にリリースされた 2BSDで 、1979年5月)、および.cshrcの処理 ユーザーがシェルをカスタマイズできるようにします(すべてのシェル、csh 、.cshrcを読み取ります スクリプトのようにインタラクティブでない場合でも)
Bourneシェルは1979年の初めにUnixV7で最初にリリースされましたが、関数のサポートはずっと後に追加され(SVR2では1984)、とにかく、rcはありませんでした。 ファイル(.profile シェル自体ではなく、環境を構成することです 。
csh Bourneシェルよりもはるかに人気がありました(Bourneシェルよりも構文が非常に悪いものでしたが)、インタラクティブに使用するためのより便利で優れた機能が多数追加されていました。
3BSDで (1980)、which cshにcshスクリプトが追加されました 実行可能ファイルの識別を支援するユーザー。これは、whichとして見つけることができるほとんど異なるスクリプトです。 最近の多くの商用Unices(Solaris、HP / UX、AIX、Tru64など)で。
そのスクリプトは、ユーザーの~/.cshrcを読み取ります (すべてのcshのように csh -fで呼び出されない限り、スクリプトは実行されます )、エイリアスのリストと$pathで指定されたコマンド名を検索します (cshの配列 $PATHに基づいて維持 。
ここに行きます:which 当時最も人気のあったシェル(およびcsh)が最初に登場しました 90年代半ばまでまだ人気がありました)。これが、本に記録され、今でも広く使用されている主な理由です。
cshの場合でも注意してください ユーザー、そのwhich cshスクリプトは、必ずしも正しい情報を提供するとは限りません。 ~/.cshrcで定義されたエイリアスを取得します 、後でプロンプトで、またはたとえばsourceによって定義したものではありません 別のcshを作成する ファイル、および(それは良い考えではありませんが)、PATH ~/.cshrcで再定義される可能性があります 。
そのwhichを実行する Bourneシェルからのコマンドは、~/.cshrcで定義されたエイリアスを引き続き検索します 、ただし、cshを使用していないために持っていない場合 、それでもおそらく正しい答えが得られるでしょう。
同様の機能は、1984年までSVR2でtypeを使用してBourneシェルに追加されませんでした。 組み込みコマンド。 (外部スクリプトではなく)組み込みであるという事実は、できることを意味します。 シェルの内部にアクセスできるため、適切な情報を(ある程度)提供します。
最初のtype コマンドはwhichと同様の問題を抱えていました コマンドが見つからなかった場合に失敗の終了ステータスを返さなかったという点でスクリプト。また、実行可能ファイルの場合、whichとは異なります 、ls is /bin/lsのようなものを出力します /bin/lsの代わりに これにより、スクリプトでの使用が難しくなりました。
Unixバージョン8(実際にはリリースされていません)のBourneシェルにはtypeがありました ビルトインの名前がwhatisに変更されました また、パラメータと印刷関数の定義についてもレポートするように拡張されました。 typeも修正されました 名前が見つからない場合に失敗を返さないという問題。
rc 、Plan9(Unixのかつての後継となる)のシェル(およびakangaのようなその派生物 およびes )whatisがあります 同様に。
Kornシェル(そのサブセットはPOSIX sh 定義は)に基づいており、80年代半ばに開発されましたが、1988年以前には広く利用可能ではなく、cshの多くが追加されました。 Bourneシェルの上部にある機能(ラインエディタ、エイリアス…)。独自のwhenceを追加しました 組み込み(typeに加えて )いくつかのオプションを取りました(-v typeを提供する -冗長な出力のように、-p 実行可能ファイルのみを検索します(エイリアス/関数は検索しません…))。
AT&TとBerkeleyの間の著作権問題に関する混乱と同時期に、いくつかの無料ソフトウェア シェルの実装は、80年代後半から90年代前半に発表されました。すべてのAlmquistシェル(ash 、BSDのBourneシェルの代わりになります)、kshのパブリックドメイン実装 (pdksh )、bash (FSFが後援)、zsh 1989年から1991年の間に発表されました。
Ashは、Bourneシェルの代わりになることを目的としていましたが、typeを持っていませんでした。 hash -vがありましたが、ずっと後まで(NetBSD1.3およびFreeBSD2.3で)組み込まれていました。 。 OSF / 1 /bin/sh typeがありました OSF /1v3.xまでは常に0を返すビルトイン。 bash whenceを追加しませんでした ただし、-pを追加しました typeのオプション パスを出力するには(type -p whence -pのようになります )および-a すべてを報告する 一致するコマンド。 tcsh whichを作成しました 組み込みで、whereを追加しました bashのように機能するコマンド のtype -a 。 zsh それらすべてを持っています。
fish shell(2005)にはtypeがあります 関数として実装されたコマンド。
which 一方、cshスクリプトはNetBSDから削除され(tcshに組み込まれていて、他のシェルではあまり使用されていないため)、機能がwhereisに追加されました。 (whichとして呼び出された場合 、whereis whichのように動作します ただし、$PATHで実行可能ファイルのみを検索します。 )。 OpenBSDおよびFreeBSDでは、which また、$PATHでコマンドを検索するCで記述されたものに変更されました。 のみ。
実装
whichの実装は数十あります 構文と動作が異なるさまざまなユニスに対するコマンド。
Linuxの場合(tcshに組み込まれているもののほかに およびzsh )いくつかの実装があります。たとえば、最近のDebianシステムでは、$PATHでコマンドを検索する単純なPOSIXシェルスクリプトです。 。
busybox whichもあります コマンド。
GNUがあります which これはおそらく最も贅沢なものです。 whichを拡張しようとします cshスクリプトは他のシェルに対して行いました:より良い答えを与えることができるように、エイリアスと関数が何であるかを伝えることができます(そして、一部のLinuxディストリビューションはbashの周りにいくつかのグローバルエイリアスを設定すると思います
zsh オペレーターが2人います 実行可能ファイルのパスに展開するには:= ファイル名の拡張 演算子と:c 履歴拡張修飾子(ここではパラメータ拡張に適用されます ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zsh 、zsh/parameters モジュールはまた、コマンドハッシュテーブルをcommandとして作成します 連想配列:
$ print -r -- $commands[ls]
/bin/ls
whatis ユーティリティ(UnixV8BourneシェルまたはPlan9rcのものを除く / es )はドキュメント専用であるため、実際には関連していません(whatisデータベース、つまりマニュアルページの概要を把握しています)。
whereis 3BSDにも追加されました whichと同時に Cで書かれていますが 、cshではありません また、実行可能ファイル、マニュアルページ、およびソースを同時に検索するために使用されますが、現在の環境に基づくものではありません。繰り返しになりますが、それは別のニーズに答えます。
現在、標準の面では、POSIXはcommand -vを指定しています および-V コマンド(POSIX.2008まではオプションでした)。 UNIXはtypeを指定します コマンド(オプションなし)。以上です(where 、which 、whence どの規格にも指定されていません。
いくつかのバージョンまで、type およびcommand -v Linux Standard Base仕様ではオプションでした。これは、たとえば、いくつかの古いバージョンのposhが理由を説明しています。 (ただし、pdkshに基づいています 両方を持っていた)どちらも持っていなかった。 command -v 一部のBourneシェル実装(Solarisなど)にも追加されました。
今日のステータス
最近の状況は、そのtype およびcommand -v ボーンのようなすべてのシェルに遍在しています(ただし、@ jarnoで指摘されているように、bashの警告/バグに注意してください。 POSIXモードでない場合、またはコメント内の以下のAlmquistシェルの一部の子孫)。 tcsh whichを使用する唯一のシェルです (typeがないため そことwhich 組み込みです)。
tcsh以外のシェル内 およびzsh 、which ~/.cshrcのいずれかに同じ名前のエイリアスまたは関数がない限り、指定された実行可能ファイルのパスを教えてくれる場合があります 、~/.bashrc または任意のシェルスタートアップファイルで、$PATHを定義していません ~/.cshrcで 。エイリアスまたは関数が定義されている場合、それについて通知する場合と通知しない場合があります。または、間違ったことを通知する場合もあります。
特定の名前ですべてのコマンドについて知りたい場合は、移植性のあるものはありません。 whereを使用します tcshで またはzsh 、type -a bashで またはzsh 、whence -a ksh93およびその他のシェルでは、typeを使用できます。 which -aと組み合わせて うまくいくかもしれません。
推奨事項
実行可能ファイルへのパス名を取得する
ここで、スクリプトで実行可能ファイルのパス名を取得するには、いくつかの注意点があります。
ls=$(command -v ls)
それを行うための標準的な方法です。
ただし、いくつかの問題があります:
- 実行可能ファイルを実行せずに実行可能ファイルのパスを知ることはできません。すべての
type、which、command -v…すべてヒューリスティックを使用してパスを見つけます。$PATHをループします コンポーネントを見つけて、実行権限を持つ最初の非ディレクトリファイルを見つけます。ただし、シェルによっては、コマンドの実行に関しては、それらの多く(Bourne、AT&T ksh、zsh、ash…)が$PATHの順序で実行するだけです。execveまで システムコールはエラーで返されません。たとえば、$PATHの場合/foo:/barが含まれていますlsを実行したい 、最初に/foo/lsを実行しようとします またはそれが失敗した場合/bar/ls。/foo/lsを実行します 実行権限がないために失敗する可能性がありますが、有効な実行可能ファイルではないなど、他の多くの理由でも失敗する可能性があります。command -v ls/foo/lsを報告します/foo/lsの実行権限がある場合 、ただしlsを実行している 実際には/bar/lsを実行する可能性があります/foo/lsの場合 は有効な実行可能ファイルではありません。 -
fooの場合 ビルトインまたは関数またはエイリアス、command -v foofooを返します 。ashのようないくつかのシェルで 、pdkshまたはzsh、fooを返す場合もあります$PATHの場合 空の文字列が含まれ、実行可能なfooがあります 現在のディレクトリ内のファイル。それを考慮に入れる必要があるかもしれないいくつかの状況があります。たとえば、ビルトインのリストはシェルの実装によって異なることに注意してください(たとえば、mount時々busyboxshに組み込まれています )、たとえばbash環境から関数を取得できます。 -
$PATHの場合 相対パスコンポーネント(通常は.)が含まれます または、両方とも現在のディレクトリを参照しているが、何でもかまいません)、シェルに応じて、command -v cmd絶対パスを出力しない場合があります。したがって、command -vを実行したときに取得するパスcdを実行すると、無効になります どこか別の場所。 - 逸話:
/opt/ast/binの場合、ksh93シェルを使用 (正確なパスはシステムによって異なる可能性がありますが)$PATHにあります 、ksh93は、いくつかの追加のビルトイン(chmod)を利用できるようにします 、cmp、cat…)、ただしcommand -v chmod/opt/ast/bin/chmodを返します そのパスが存在しない場合でも。
コマンドが存在するかどうかの判断
特定のコマンドが標準で存在するかどうかを確認するには、次のようにします。
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
whichを使用したい場合
(t)csh
cshで およびtcsh 、選択肢はあまりありません。 tcshで 、whichとしては問題ありません ビルトインです。 cshで 、それはシステムのwhichになります コマンド。場合によっては、希望どおりの結果が得られないことがあります。
一部のシェルでのみコマンドを検索
whichを使用することが理にかなっている場合 bashの潜在的なシェルビルトインまたは関数を無視して、コマンドのパスを知りたい場合です。 、csh (tcshではありません )、dash 、またはBourne シェルスクリプト、つまりwhence -pを持たないシェル (kshのように またはzsh )、command -ev (yashのように )、whatis -p (rc 、akanga )または組み込みのwhich (tcshのように またはzsh )whichが存在するシステム 利用可能であり、cshではありません スクリプト。
これらの条件が満たされている場合:
echo=$(which echo)
最初のechoのパスが表示されます $PATH内 (コーナーケースを除く)echoかどうかに関係なく、 また、シェルビルトイン/エイリアス/関数であるかどうかもわかりません。
他のシェルでは、次のことをお勧めします:
- zsh :
echo==echoまたはecho=$commands[echo]またはecho=${${:-echo}:c} - ksh 、 zsh :
echo=$(whence -p echo) - ヤシュ :
echo=$(command -ev echo) - rc 、アカンガ :
echo=`whatis -p echo`(スペースのあるパスに注意してください) - 魚 :
set echo (type -fp echo)
実行するだけの場合は注意してください そのecho コマンド、パスを取得する必要はありません。次の操作を実行できます。
env echo this is not echoed by the builtin echo
たとえば、tcsh 、組み込みのwhichを防ぐため 使用から:
set Echo = "`env which echo`"
外部コマンドが必要な場合
whichを使用したい別のケース 実際に必要するときです 外部コマンド。 POSIXでは、すべてのシェルビルトイン(commandなど)が必要です。 )外部コマンドとしても利用できますが、残念ながら、commandには当てはまりません。 多くのシステムで。たとえば、commandを見つけることはめったにありません Linuxベースのオペレーティングシステムでコマンドを実行しますが、ほとんどのオペレーティングシステムにはwhich コマンド(ただし、オプションと動作が異なるものもあります)。
外部コマンドが必要になる可能性があるのは、POSIXシェルを呼び出さずにコマンドを実行する場合です。
system("some command line") 、popen() …Cまたはさまざまな言語の関数は、シェルを呼び出してそのコマンドラインを解析するため、system("command -v my-cmd") それらで動作します。例外はperlです。 これは、シェルの特殊文字(スペース以外)が表示されない場合にシェルを最適化します。これは、バックティック演算子にも当てはまります:
$ perl -le 'print system "command -v emacs"'
-1
$ perl -le 'print system ":;command -v emacs"'
/usr/bin/emacs
0
$ perl -e 'print `command -v emacs`'
$ perl -e 'print `:;command -v emacs`'
/usr/bin/emacs
その:; 上記はperlを強制します そこでシェルを呼び出します。 whichを使用する 、そのトリックを使用する必要はありません。