「which」を使用しないのはなぜですか?それでは何を使用しますか?


328

実行ファイルへのパスを探したり、Unixシェルでコマンド名を入力する場合に何が起こるか確認する場合は、異なるユーティリティの茄多があります(whichtypecommandwhencewherewhereiswhatishash、など)。

which避けるべきだとよく耳にします。どうして?代わりに何を使うべきですか?


3
使用に反対するほとんどの議論whichは、対話型のシェルコンテキストを想定していると思います。この質問には/ portabilityというタグが付けられています。したがって、このコンテキストの質問whichは、$PATH「特定の名前の最初の実行可能ファイルを見つけるのではなく、何を使用するか」と解釈します。ほとんどの答えと理由whichは、ほとんどの実世界のポータブルシェルスクリプトで学術的な関心事であるエイリアス、組み込み関数、および関数を処理します。ローカルで定義されたエイリアスは、シェルスクリプトの実行時に継承されません(ソースコードを使用してソースを作成する場合を除く.)。
MattBianco

5
@MattBianco、はい、csh(そしてwhichまだcshほとんどの商用Unix系OS上でスクリプト)を読んでない~/.cshrcとき非対話型。そのため、cshスクリプトは通常で始まることに気付くでしょう#! /bin/csh -f。のエイリアスを提供するwhichことを目的としているためではありませんcsh。POSIXシェルユーザーにはcommand -v
ステファンシャゼル

@rudimeier、あなたのシェルが(t)csh(または正しい結果を与えない場合は気にしない)、typeまたはcommand -v代わりに使用しない限り、答えは常にあります理由については回答をご覧ください。
ステファンシャゼラス

1
@rudimeierは、(stat $(which ls)いくつかの理由(不足しているために間違っている--、不足している引用符)、の使用だけではなくwhich)。を使用しますstat -- "$(command -v ls)"。これはls、実際にはファイルシステム上にあるコマンド(シェルの組み込みコマンドやエイリアスの関数ではない)であると想定しています。whichあなたに間違ったパス(ないあなたのシェルが入力した場合に実行でしょうパス与えるかもしれないls)、またはいくつかの他のシェルの設定で定義されているあなたにエイリアスを与える...
ステファンChazelas

1
@rudimeier、繰り返しますが、多くのwhich実装ではls$PATHlsシェルで起動するものに関係なく)のルックアップで見つかるものさえも与えない条件がいくつかあります。sh -c 'command -v ls'、またはzsh -c 'rpm -q --whatprovides =ls'あなたに正しい答えを与える可能性が高いです。ここのポイントは、それwhichが壊れた遺産だということですcsh
ステファンシャゼラス

回答:


366

以下は、あなたがそれについて知りたくないと思ったことのないすべてです。

概要

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

インタラクティブなBourneのようなシェルのプロンプトで:

type ls

このwhichコマンドはCシェルの壊れた遺産であり、Bourneのようなシェルにはそのままにしておく方が良いでしょう。

ユースケース

スクリプトの一部として、またはシェルプロンプトで対話的に情報を検索することには違いがあります。

シェルプロンプトでの一般的な使用例は次のとおりです。このコマンドの動作がおかしいのですが、正しいコマンドを使用していますか?入力したときに正確に何が起こったのmycmdですか?それが何であるかをさらに調べることはできますか?

その場合、実際にコマンドを呼び出さずにコマンドを呼び出したときにシェルが何をするかを知りたいと思うでしょう。

シェルスクリプトでは、まったく異なる傾向があります。シェルスクリプトでは、実行したいだけでコマンドがどこにあるか、または何であるかを知りたい理由はありません。一般に、知りたいのは実行可能ファイルのパスです。そのため、より多くの情報を取得できます(そのファイルに関連する別のファイルへのパス、またはそのパスにある実行可能ファイルのコンテンツから情報を読み取る)。

対話的に、システムで使用可能なすべてmy-cmdコマンドをスクリプトで知りたい場合があります。

利用可能なツールのほとんどは(多くの場合そうであるように)インタラクティブに使用されるように設計されています。

歴史

最初に少し歴史。

70年代後半までの初期のUnixシェルには、関数やエイリアスがありませんでした。従来の実行可能ファイルの検索のみ$PATHcsh1978年頃にエイリアスを導入しました(ただし、1979年5月cshにで最初にリリースされました2BSD)。また、.cshrcユーザーがシェルをカスタマイズするための処理(すべてのシェル、スクリプトのようにインタラクティブではない場合でもcsh読み取り.cshrc)。

Bourneシェルは1979年の早い時期にUnix V7で最初にリリースされましたが、関数サポートはかなり後で追加され(SVR2では1984年)、とにかく、rcファイルがありません(.profileシェル自体ではなく環境を構成するためです)。

csh (Bourneシェルよりもひどく悪い構文でしたが)Bourneシェルよりもはるかに人気があり、インタラクティブに使用するためのより便利で素晴らしい機能がたくさん追加されました。

3BSD(1980)、whichCSHスクリプトが追加されましたcsh実行可能ファイルを識別しやすくするために、ユーザー、そしてそれはあなたのように見つけることができるほとんど変わらないスクリプトですwhich、今日は(Solaris、HP / UX、AIXまたはのTru64など)多くの商用のUnix上。

そのスクリプトはユーザーの~/.cshrc(すべてのcshスクリプトがで呼び出されない限り行うように)を読み取り、csh -fエイリアスのリストと$path(にcsh基づいて維持する配列)で提供されたコマンド名を検索し$PATHます。

ここwhichで、最も人気のあるシェルで最初に登場しました(そしてcsh90年代半ばまでまだ人気がありました)。これが本で文書化され、今でも広く使用されている主な理由です。

cshユーザーであっても、そのwhichcshスクリプトは必ずしも正しい情報を提供するとは限らないことに注意してください。それはで定義されたエイリアスを取得します。~/.cshrcプロンプトで、またはsource別のcshファイルを使用して後で定義したエイリアスではなく、(それは良い考えでPATHはありませんが)で再定義され~/.cshrcます。

whichBourneシェルからそのコマンドを実行すると、で定義されたエイリアスを引き続き検索しますが~/.cshrc、を使用していないためエイリアスがない場合はcsh、おそらく正しい答えが得られます。

type組み込みコマンドを使用したSVR2の1984年まで、同様の機能はBourneシェルに追加されませんでした。(外部スクリプトとは対照的に)組み込みであるという事実は、シェルの内部にアクセスできるため、(ある程度)適切な情報を提供できることを意味します。

最初のtypeコマンドにはwhich、コマンドが見つからない場合に失敗終了ステータスを返さないという点で、スクリプトと同様の問題がありました。また、実行可能ファイルの場合、とは反対にwhich、スクリプトでの使用が簡単になったls is /bin/lsだけ/bin/lsではなく、次のようなものを出力します。

Unixバージョン8(type非公開)Bourneシェルでは、組み込みの名前がに変更されましたwhatis。そして、Plan9の(のUnix一度に-こと後継)シェルrc(などその誘導体akangaとはes)持っているwhatisとしても。

80年代半ばに開発されたが1988年以前に広く利用できなかったKornシェル(POSIX sh定義のベースとなっているサブセットcsh)は、Bourneシェルの上に多くの機能(ラインエディター、エイリアス...)を追加しました。whence(-に加えてtype)独自の組み込みを追加し、いくつかのオプションを取りました(-likeのような詳細な出力-vを提供し、実行可能ファイル(エイリアス/関数ではなく...)のみを検索します)。type-p

AT&TとBerkeleyの間の著作権の問題に関する混乱と同時期に、80年代後半から90年代初頭にいくつかのフリーソフトウェアシェルの実装が登場しました。すべてのAlmquistシェル(BSD、Bourneシェルの代替)、ksh(pdksh)のパブリックドメイン実装(bashFSFがスポンサー)は、zsh1989年から1991年の間に登場しました。

Ashは、Bourneシェルの代わりになることを意図していましたが、typeずっと後(NetBSD 1.3およびFreeBSD 2.3)になるまで組み込まれていませんでしたhash -v。OSF / 1に/bin/shは、typeOSF / 1 v3.xまで常に0を返すビルトインがありました。bashは追加しませんでしたwhenceが、パスを印刷する-pオプションtype(のtype -pようになりますwhence -p)および一致するすべてのコマンド-aを報告するオプションを追加しました。作られた組み込み、コメントを追加しましたような演技コマンドのを。それらがすべてあります。tcshwhichwherebashtype -azsh

fishシェル(2005)はtype関数として実装コマンド。

which一方、cshスクリプトはNetBSDから削除され(tcshに組み込まれており、他のシェルではあまり使用されないため)、追加された機能whereis(として呼び出されるとwhich、でのみ実行可能ファイルを検索すること以外whereisは同様に動作whichします$PATH)。OpenBSDおよびFreeBSDでも、whichコマンド$PATHのみを検索するCで記述されたものに変更されました。

実装

whichさまざまな構文と動作を備えたさまざまなUnicesには、コマンドの実装が多数あります。

Linuxでは(とに組み込まれているもののほかtcshzsh)いくつかの実装があります。たとえば、最近のDebianシステムでは、のコマンドを探す単純なPOSIXシェルスクリプトです$PATH

busyboxwhichコマンドもあります。

ありGNU which、おそらく最も贅沢な1です。それはどのような拡張しようとするwhichのcshスクリプトが他のシェルにした:あなたはそれがあなたのより良い答えを与えることができます(と私はいくつかのLinuxディストリビューションは、のためにその周りにいくつかのグローバルエイリアスを設定すると考えているように、あなたのエイリアスや関数が何であるか、それを伝えることができるbashことを行います) 。

zshには、実行可能ファイルのパスに展開する2つの演算子があります。= ファイル名展開演算子と:c履歴展開修飾子(ここではパラメーター展開に適用されます):

$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls

zshzsh/parametersモジュールでは、commands連想配列としてコマンドハッシュテーブルも作成します。

$ print -r -- $commands[ls]
/bin/ls

whatis(UnixのV8 Bourneシェルまたはプラン9の1を除き、ユーティリティrc/がes)実際にそれが唯一のドキュメンテーションのためだとは関係ありません(whatisデータベースをgrepする、それはmanページの概要です)。

whereisまた、現在の環境に基づいていないが、実行可能ファイル、マニュアルページ、およびソースを同時に検索するために使用され、同時に検索されないように記述され3BSDwhichいるように、同時に追加されました。繰り返しますが、それは別のニーズに答えます。Ccsh

現在、標準的な面では、POSIXはcommand -vand -Vコマンド(POSIX.2008まではオプションでした)を指定します。UNIXはtypeコマンドを指定します(オプションなし)。それはすべてです(wherewhichwhence任意の標準で規定されていません)

あるバージョンまででtypeありcommand -v、Linux Standard Base仕様ではオプションでした。これにより、たとえばposh(旧バージョンと旧バージョンのpdksh両方の)一部の旧バージョンがどちらもなかった理由が説明されます。command -vいくつかのBourneシェル実装(Solarisなど)にも追加されました。

今日のステータス

最近は状況がそれであるtypecommand -v(で警告/バグに注意し、@jarnoで述べたように、しかし、すべてのBourneのようなシェルに遍在しているbashPOSIXモードまたはAlmquistのいくつかの子孫でコメントで以下のシェルでないときに)。tcsh使用したい唯一のシェルですwhich(ないtypeのでwhich組み込みです)。

以外のシェルにtcshzshwhich限りそこにはエイリアスや関数が同じ名前によって私達のいずれかでませんよう、あなたに与えられた実行ファイルのパスを伝えること~/.cshrc~/.bashrcまたは任意のシェル起動ファイルは、あなたが定義していない$PATHあなたに~/.cshrc。エイリアスまたは関数が定義されている場合、それについて通知されたり、されなかったり、間違ったことを伝えたりする場合があります。

特定の名前のすべてのコマンドについて知りたい場合、移植性のあるものはありません。あなたは使うだろうwheretcshzshtype -abashzshwhence -aは、ksh93にし、他のシェルでは、使用することができますtypeとの組み合わせでwhich -a動作する可能性があります。

推奨事項

実行可能ファイルへのパス名を取得する

ここで、スクリプトで実行可能ファイルのパス名を取得するには、いくつかの注意事項があります。

ls=$(command -v ls)

それを行う標準的な方法でしょう。

ただし、いくつかの問題があります。

  • 実行せずに実行可能ファイルのパスを知ることはできません。すべてのtypewhichcommand -v...すべてのパスを見つけるために、ヒューリスティックを使用しています。$PATHコンポーネントをループし、実行権限を持つ最初の非ディレクトリファイルを見つけます。ただし、シェルによっては、コマンドの実行に関しては、それらの多く(Bourne、AT&T ksh、zsh、ash ...)は$PATHexecveシステムコールがエラーを返さないまでの順序で実行されます。 。たとえば、$PATHcontains /foo:/barで実行したい場合ls、最初に実行しようとする/foo/lsか、失敗した場合/bar/lsです。今の実行/foo/ls実行許可がないために失敗する場合がありますが、有効な実行可能ファイルではないなど、他の多くの理由もあります。command -v ls報告する/foo/lsあなたがの実行権限を持っている場合/foo/ls、しかし、実行しているls実際に実行する可能性がある/bar/ls場合には/foo/ls有効な実行可能ではありません。
  • 場合foo組み込みまたは関数またはエイリアスをある、command -v foo返しますfooashpdkshまたはのような一部のシェルでは、空の文字列が含まれていて、現在のディレクトリに実行可能ファイルがある場合にzshも返さfooれることがあります。それを考慮する必要がある状況がいくつかあります。たとえば、組み込みのリストはシェルの実装によって異なり(たとえば、busyboxの組み込みである場合があります)、たとえば環境から関数を取得できることに注意してください。$PATHfoomountshbash
  • $PATH相対パスコンポーネント(通常、.または両方とも現在のディレクトリを参照しますが、任意の空の文字列)が含まれている場合、シェルによってはcommand -v cmd、絶対パスが出力されない場合があります。そのため、実行時に取得したパスは、どこか別の場所ではcommand -v無効になりcdます。
  • 逸話:は、ksh93シェルで、あれば/opt/ast/bin(つまり、正確なパスは、私は信じて異なるシステムで異なることができますが)があなたの中にある$PATH、は、ksh93は、いくつかの余分なビルトイン(利用できるようになりますchmodcmpcat...)が、command -v chmod返され/opt/ast/bin/chmod、そのパスが」doesnの場合でも、存在します。

コマンドが存在するかどうかの判断

特定のコマンドが標準的に存在するかどうかを確認するには、次のようにします。

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

cshtcsh、あなたは多くの選択肢を持っていません。ではtcshwhich組み込みで問題ありません。でcsh、それはシステムwhichコマンドになりますが、いくつかのケースであなたが望むことをしないかもしれません。

一部のシェルでのみコマンドを見つける

使用するのwhichが理にかなっているケースは、コマンドのパスを知りたい場合、潜在的なシェル組み込み関数、またはbashcsh(ではないtcsh)、、dashまたはBourneシェルスクリプト、つまりwhence -pkshまたはのようなzsh)シェルスクリプトを無視する場合です、command -ev(のようなyash)、whatis -prcakanga)、またはスクリプトではない利用可能なシステムの組み込みwhichtcshまたはのようなzsh)。whichcsh

これらの条件が満たされている場合、次のようになります。

echo=$(which echo)

あなたの最初のパス与えるecho中を$PATH(コーナーケースを除いて)に関係なく、かどうかのecho、シェルの組み込み/エイリアス/機能かどうかであることを起こります。

他のシェルでは、次をお勧めします。

  • zshのecho==echoまたはecho=$commands[echo]またはecho=${${:-echo}:c}
  • kshzshecho=$(whence -p echo)
  • ヤシュecho=$(command -ev echo)
  • rcakanga:(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多くのシステムではそうではありません。たとえば、commandLinuxベースのオペレーティングシステムでコマンドを見つけることはほとんどありませんが、ほとんどの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、あなたはそのトリックを使用する必要はありません。


24
@Joe whichは、csh多くの商用Unicesのスクリプトです。その理由は歴史的なものです。だからこそ、私は歴史を語ったので、人々はそれがどこから来たのか、なぜ人々がそれを使い慣れたのか、そして実際にあなたがそれを使うべき理由がないのかを理解しています。はい、一部の人々は(t)cshを使用しています。いない誰もがまだのLinuxを使用しています
ステファンChazelas

12
この投稿を読んだ後、私は答えの多くのコンテキストを見つけましたが、答え自体は見つけませんでした。どこにこの記事では、それが実際になぜ言わないではない使用することをwhichあなたが使用しようとする可能性があるものとは反対に、which、やっての歴史which、の実装which、他の関連するタスクを実行するコマンド、または実際に使用する理由はwhich?なぜ他のコマンドが優れているのですか?彼らは何と違うのwhichですか?落とし穴をどのように回避しますか?この答えは実際には代替案の問題についてより多くの言葉を費やしていますwhich
user62251 14年

1
答えが主張するものとは反対にcommand -v、少なくともパスなしで純粋なファイル名引数で呼び出す場合は、実行許可をチェックしません。dash 0.5.8とGNU bash 4.3.48でテストしました。
ジャーノ

2
@StéphaneChazelasで新しいファイルを作成してtouch /usr/bin/mytestfileから実行command -v mytestfileすると、パスが提供されます(そうではありwhich mytestfileません)。
ヤルノ

2
@jarno、はい、そうです。bashそれは実行可能なものを見つけることができない場合は非実行可能ファイルに落ち着くので、「OK」ですます(ただし実際には1ではなくなりcommand -v/ typeエラーを返す)それはそれはあなたが実行したときに実行しようとするコマンドだとしてmytestfile、しかし、dash実行可能ファイルのcmd前に非実行可能ファイルがあるかのように振る舞いはバグがあり、command -v実行中cmdは実行可能ファイルを実行します(間違ったものもハッシュされます)。FreeBSD sh(これもに基づいていますash)にも同じバグがあります。zsh、yash、ksh、mksh、bashはshで問題ありません。
ステファンシャゼラス

47

使用したくない理由whichはすでに説明されていますが、which実際に障害が発生するいくつかのシステムの例をいくつか示します。

Bourneのようなシェルではwhich、の出力とtypetypeシェル組み込みコマンドであり、コマンドを呼び出す方法を伝えるシェルであるため、グラウンドトゥルースになることを意図しています)の出力と比較しています。

多くのケースはコーナーケースですが、which/ typeがコーナーケースでよく使用されることを念頭に置いてください(このようなコマンドがなぜそのように振る舞うのかなど、予期しない動作に対する答えを見つけるために、私はどちらを呼んでいますか?

ほとんどのシステム、ほとんどのBourneのようなシェル:関数

最も明らかなケースは関数の場合です:

$ type ls
ls is a function
ls ()
{
[ -t 1 ] && set -- -F "$@";
command ls "$@"
}
$ which ls
/bin/ls

その理由は、ということでwhichのみエイリアス(のではないいつものものかかわらについて時々実行ファイルについて報告し、自分の殻)、ない機能。

GNUのmanページが壊れている(引用するのを忘れていたため$@)ので、関数をレポートする方法の例もありますが、エイリアスのように、シェル構文パーサーを実装していないので、だまされやすいです:

$ which() { (alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot "$@";}
$ f() { echo $'\n}\ng ()\n{ echo bar;\n}\n' >> ~/foo; }
$ type f
f is a function
f ()
{
echo '
}
g ()
{ echo bar;
}
' >> ~/foo
}
$ type g
bash: type: g: not found
$ which f
f ()
{
echo '
}
$ which g
g ()
{ echo bar;
}

ほとんどのシステム、ほとんどのBourneのようなシェル:組み込み

別の明らかなケースは、組み込みwhichコマンドまたはキーワードです。外部コマンドであるため、シェルがどの組み込みコマンドを持っているか(およびのような一部のシェルzshbashまたは組み込みコマンドをksh動的にロードできる)を知る方法がありません。

$ type echo . time
echo is a shell builtin
. is a shell builtin
time is a shell keyword
$ which echo . time
/bin/echo
which: no . in (/bin:/usr/bin)
/usr/bin/time

(組み込みのzsh場所にwhichは適用されません)

Solaris 10、AIX 7.1、HP / UX 11i、Tru64 5.1、およびその他の多く:

$ csh
% which ls
ls:   aliased to ls -F
% unalias ls
% which ls
ls:   aliased to ls -F
% ksh
$ which ls
ls:   aliased to ls -F
$ type ls
ls is a tracked alias for /usr/bin/ls

これは、ほとんどの商用Unices which(3BSDの元の実装のように)がcshを読み取るスクリプトであるためです~/.cshrc。報告するエイリアスは、現在定義しているエイリアスや実際に使用しているシェルに関係なく、そこに定義されているものです。

HP / UXまたはTru64の場合:

% echo 'setenv PATH /bin:/usr/bin' >> ~/.cshrc
% setenv PATH ~/bin:/bin:/usr/bin
% ln -s /bin/ls ~/bin/
% which ls
/bin/ls

(SolarisおよびAIXバージョンは、コマンドを検索する前$pathに読み取り、~/.cshrc復元する前に保存することにより、この問題を修正しました)

$ type 'a b'
a b is /home/stephane/bin/a b
$ which 'a b'
no a in /usr/sbin /usr/bin
no b in /usr/sbin /usr/bin

または:

$ d="$HOME/my bin"
$ mkdir "$d"; PATH=$PATH:$d
$ ln -s /bin/ls "$d/myls"
$ type myls
myls is /home/stephane/my bin/myls
$ which myls
no myls in /usr/sbin /usr/bin /home/stephane/my bin

(もちろん、cshスペースを含む引数で動作することは期待できないスクリプトです...)

CentOS 6.4、bash

$ type which
which is aliased to `alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
$ alias foo=': "|test|"'
$ which foo
alias foo=': "|test|"'
        /usr/bin/test
$ alias $'foo=\nalias bar='
$ unalias bar
-bash: unalias: bar: not found
$ which bar
alias bar='

そのシステムには、GNU whichコマンドをラップするシステム全体で定義されたエイリアスがあります。

偽の出力があるためであるwhichの出力読み込みbashs「をaliasしかし、それを正しく解析する方法を知らないとヒューリスティックを使用します(1行に1つのエイリアス、最初は後にコマンドを見つけ探し|;&...)

CentOSの最悪な点zshは、完全に素晴らしいwhich組み込みコマンドを持っていることですが、CentOSはそれをGNUの動作しないエイリアスに置き換えることでそれを破ることができましたwhich

Debian 7.0、ksh93:

(ただし、多くのシェルを備えたほとんどのシステムに適用されます)

$ unset PATH
$ which which
/usr/local/bin/which
$ type which
which is a tracked alias for /bin/which

Debianでは/bin/which/bin/shスクリプトです。私の場合、sh存在しますdashが、それは同じbashです。

解除はPATH無効にすることではないPATHのルックアップを、しかし、システムの使用を意味し、デフォルトのPATH残念ながらDebianの上で、誰もが上同意していない(dashbash持って/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binzsh持っている/bin:/usr/bin:/usr/ucb:/usr/local/binksh93持っている/bin:/usr/binmksh持っている/usr/bin:/bin$(getconf PATH)、)execvp()のように(env)がある:/bin:/usr/binはい、まず現在のディレクトリを検索します(! ))。

理由であるwhich、使用していますので、上記のそれは間違って取得するdashのデフォルトをPATH異なるであるksh93S '

以下whichを報告するGNUの方が優れているわけではありません。

which: no which in ((null))

(興味深いことに、実際に/usr/local/bin/whichは私のシステム上に実際にakanga付属するスクリプトがあります(デフォルトはでakangaあるrcシェル派生物))PATH/usr/ucb:/usr/bin:/bin:.

bash、任意のシステム:

1つのクリスは彼の答えにを参照しています

$ PATH=$HOME/bin:/bin
$ ls /dev/null
/dev/null
$ cp /bin/ls bin
$ type ls
ls is hashed (/bin/ls)
$ command -v ls
/bin/ls
$ which ls
/home/chazelas/bin/ls

また、hash手動で呼び出した後:

$ type -a which
which is /usr/local/bin/which
which is /usr/bin/which
which is /bin/which
$ hash -p /bin/which which
$ which which
/usr/local/bin/which
$ type which
which is hashed (/bin/which)

そして、which時々type失敗するケース:

$ mkdir a b
$ echo '#!/bin/echo' > a/foo
$ echo '#!/' > b/foo
$ chmod +x a/foo b/foo
$ PATH=b:a:$PATH
$ which foo
b/foo
$ type foo
foo is b/foo

さて、いくつかのシェルで:

$ foo
bash: ./b/foo: /: bad interpreter: Permission denied

他の人と:

$ foo
a/foo

実行できないことを事前に知ることwhichtypeできませんb/foo。いくつかのシェルのようなbashkshまたはyash、起動時にfoo実際に実行しようとするb/foo他の人が(のような一方で、エラーを報告しzshashcshBournetcsh)実行するa/fooの障害時execve()にシステムコールb/foo


mksh実際にはデフォルトに異なるものを使用します$PATH:最初に、オペレーティングシステムのコンパイル時定数_PATH_DEFPATHが使用され(最も一般的にはBSDで)、次にconfstr(_CS_PATH, …)使用され(POSIX)、両方が存在しないか失敗した場合に/bin:/usr/bin:/sbin:/usr/sbin使用されます。
ミラビロス

1
最初の例では、たとえPATHからls使用lsしている関数であっても。そして、whichどちらが使用されている/usr/bin/ls かを教えて大丈夫です/usr/local/bin/ls。「使用しない理由」が表示されない
....-rudimeier

@rudimeier、which ls私を与える/bin/lsかどうかにかかわらずのls関数呼び出していない/bin/lsか、/opt/gnu/bin/lsまたはdirまったく何もか。IOW、which無関係な何か与えている(これは実装、IMMVこと)
ステファンChazelas

1
@StéphaneChazelas。ダメダメダメ。私すでにls関数であることを知っています。私の関数がから呼び出していることを知っていlsます。今、ファイルがどこにあるか私に語りました。「シェルがこのコマンドで何をするか」という1つのユースケースのみが表示されます。このユースケースは間違っています、正しいです。しかし、(GNU)がまさに正しいものである他のユースケースもあります。lsPATHwhichwhichwhich
-rudimeier

@rudimeter、which実装に依存します。エイリアスであると言う人もいます(エイリアスが設定されている場合、または~/.cshrcそのようなエイリアスを持つホームがある場合)、ある条件下では間違ったパスを提供する人もいます。sh -c 'command -v ls'、完璧ではありませんが、その異なる要件に対する正しい答えを提供する可能性が高くなります(標準でもあります)。
ステファンシャゼラス

21

(私の簡単なスキムから)ステファンが言及しなかったように思われることの1つはwhich、シェルのパスハッシュテーブルについて何も知らないことです。これは、実際に実行されていることを表していない結果を返す可能性があり、デバッグでは無効になります。


6

UNIXの精神では: 各プログラムに1つのことをうまくさせます。

答えが目標の場合: この名前の実行可能ファイルはどれですか?

Debianシステムで提供されるどの実行可能プログラムが良い答えです。cshで提供されたにはエイリアスが含まれていたため、問題の原因になりました。一部のシェルが内部ビルトインとして提供するものには、異なる目標があります。その実行可能ファイルを使用するか、この回答の最後に記載されているスクリプトを使用してください。

このスクリプトを使用する場合、答えは簡潔でシンプルで便利です。

この目標は、質問の最初の文と一致します。

実行可能ファイルへのパスを探しているとき……

実行可能ファイル(ほとんどのLinuxシステムには実行可能ファイル)がないシステムがある場合は、~/bin/which以前/bin/にPATH に作成して、この投稿の下部にあるようなシステム実行可能ファイルを上書きします。

その実行可能ファイルは(デフォルトで)PATHで見つかったすべての実行可能ファイルをリストします。最初のもののみが必要な場合、オプション-fを使用できます。


この時点で、別の目標に分類されます。

シェルが実行するもの(解析後)

それは2番目の文から来ています:

Unixシェルでコマンド名を入力するとどうなるかを確認する

この2番目の主題は、答えるのがかなり難しい質問に対する良い答えを見つけようとします。シェルには、異なるビュー、コーナーケース、および(少なくとも)異なる解釈があります。それに追加:

さまざまなユーティリティがあります(which、type、command、whence、where、whereis、whatis、hashなど)。

確かに、すべての試みはその目標に一致します。


どれを避けますか?

避けるべきだとよく耳にします。

私は不思議に思う:(which少なくともdebianで)問題なく動作するのなら、なぜそう言うべきなのか?

UNIXの精神:各プログラムに1つのことをうまくさせます。

外部プログラムは、which一つのことをやっている:同じ名前を持つPATHの最初の実行可能な検索コマンド名を。そして、それを合理的にうまくやっています。

この質問にもっと根本的な方法で答える他のプログラムやユーティリティは知りません。そのため、これは有用であり、必要なときに使用できます。

最も近い代替案は次のように思われますcommand -pv commandNameが、ビルトインとエイリアスについても報告します。同じ答えではありません。

もちろん、which制限はありますが、すべての質問に答えるわけではなく、それを行うことができるツールはありません(まあ、まだ …)。ただし、回答するように設計された質問(上記の質問)の回答に使用すると便利です。似たようなものedが限られていてsed登場しました(またはvi/ vim)。または、awk制限があり、Perlを表示および拡張しました。しかし、edsedまたは/およびawk特定のユースケース持っていvimたりperlしているではない最高のツールを。

どうして?

おそらくwhich、シェルユーザーが尋ねる可能性のある質問の一部のみに回答するためです。

commandNameを入力すると何が実行されますか?


外部

これは、多くのシステムで外部実行可能ファイルとして利用可能でなければなりません。
その外部ツールを呼び出す唯一の確実な方法は、envを使用してシェルから出てから呼び出すことですwhich(すべてのシェルで機能します)。

 $ env which which
 /usr/bin/which

または、フルパスを使用しますwhich(システムによって異なる場合があります)。

 /usr/bin/which which 

なぜそれがhack必要ですか?いくつかのシェル(特にzsh)が非表示になるためwhich

 $ zsh -c 'which which'
 which: shell built-in command

されている外部ツール(のようにenv、それはシェルの内部情報を報告しない理由)完全に説明しています。エイリアス、関数、ビルトイン、特別なビルトイン、シェル(エクスポートされていない)変数など:

 $ env which ls
 /usr/bin/ls
 $ env which ll       # empty output

ll(の一般的なエイリアスll='ls -l')の空の出力はll、実行可能プログラムに関連していないこと、または少なくともllPATHで指定された実行可能ファイルがないことを示します。の使用はll、他の何か、この場合はエイリアスを呼び出す必要があります:

 $ type ll
 ll is aliased to `ls -l'

type そして command

コマンドtypeおよびcommand -vはPOSIXによって要求されます。これらはほとんどのシェルで動作することが期待されており、csh、tcsh、fish、rcを除き動作します。

両方のコマンドを使用して、実行するコマンドの別の視点を提供できます。

whencewherewhereiswhatishash

すると、そこにあるwhencewherewhereiswhatishash、およびいくつかの他。同様の質問に対するすべての異なる回答。すべて異なるシェルで異なる方法で動作します。おそらく、whence後に最も一般的typeです。その他は、同じ質問にさまざまな方法で答える特別なソリューションです。

代わりに何を使うべきですか?

おそらくwhichの名前で実行が存在する場合、最初の知っているコマンド名、その後、typecommandあれば、その後、コマンド名は:まだ見つかっていない whencewherewhereiswhatishashの順に。


which実行可能ファイルを提供するシェルスクリプト。

#! /bin/sh
set -ef; oldIFS=$IFS; IFS=:

say()( IFS=" "; printf "%s\n" "$*"; )
say "Simplified version of which."
usage(){ say Usage: "$0" [-f] args; }
if [ "$#" -eq 0 ]; then say Missing argument(s); usage; exit 2; fi

firstmatch=0
while getopts f whichopts; do
    case "$whichopts" in
        f) firstmatch=1 ;;
        ?) usage; exit 3 ;;
    esac
done
[ "$OPTIND" -gt 1 ] && shift `expr "$OPTIND" - 1`

allret=0; [ "$#" -eq 0 ] && allret=1
for program in "$@"; do
    ret=1
    for element in $PATH''; do
        case "$program" in
            */*) element="$program"; loop=0;;
            *)   element="${element:-.}/$program"; loop=1;;
        esac
        if [ -f "$element" ] && [ -x "$element" ]; then
            say "$element"
            ret=0
            if [ "$firstmatch" -eq 1 ] || [ "$loop" -eq 0 ]; then break; fi
        fi
    done
    [ "$ret" -eq 1 ] && allret=1
done

IFS="$oldIFS"
exit "$allret"

0

避けるべきだとよく耳にします。どうして?代わりに何を使うべきですか?

私はそれを聞いたことがありません。特定の例を提供してください。私はあなたのLinuxディストリビューションとインストールされたソフトウェアパッケージについて心配していますwhich

SLES 11.4 x86-64

tcshバージョン6.18.01の場合:

> which which

which: shell built-in command.

bashバージョン3.2-147の場合:

> which which

/usr/bin/which

> which -v

GNU which v2.19, Copyright (C) 1999 - 2008 Carlo Wood.
GNU which comes with ABSOLUTELY NO WARRANTY;
This program is free software; your freedom to use, change
and distribute this program is protected by the GPL.

which一部である のutil-linuxの Linuxオペレーティングシステムの一部として使用するためのLinuxカーネルの組織で配布標準パッケージ。これらの他のファイルも提供します

/bin/dmesg
/bin/findmnt
/bin/logger
/bin/lsblk
/bin/more
/bin/mount
/bin/umount
/sbin/adjtimex
/sbin/agetty
/sbin/blkid
/sbin/blockdev
/sbin/cfdisk
/sbin/chcpu
/sbin/ctrlaltdel
/sbin/elvtune
/sbin/fdisk
/sbin/findfs
/sbin/fsck
/sbin/fsck.cramfs
/sbin/fsck.minix
/sbin/fsfreeze
/sbin/fstrim
/sbin/hwclock
/sbin/losetup
/sbin/mkfs
/sbin/mkfs.bfs
/sbin/mkfs.cramfs
/sbin/mkfs.minix
/sbin/mkswap
/sbin/nologin
/sbin/pivot_root
/sbin/raw
/sbin/sfdisk
/sbin/swaplabel
/sbin/swapoff
/sbin/swapon
/sbin/switch_root
/sbin/wipefs
/usr/bin/cal
/usr/bin/chrp-addnote
/usr/bin/chrt
/usr/bin/col
/usr/bin/colcrt
/usr/bin/colrm
/usr/bin/column
/usr/bin/cytune
/usr/bin/ddate
/usr/bin/fallocate
/usr/bin/flock
/usr/bin/getopt
/usr/bin/hexdump
/usr/bin/i386
/usr/bin/ionice
/usr/bin/ipcmk
/usr/bin/ipcrm
/usr/bin/ipcs
/usr/bin/isosize
/usr/bin/line
/usr/bin/linux32
/usr/bin/linux64
/usr/bin/look
/usr/bin/lscpu
/usr/bin/mcookie
/usr/bin/mesg
/usr/bin/mkzimage_cmdline
/usr/bin/namei
/usr/bin/rename
/usr/bin/renice
/usr/bin/rev
/usr/bin/script
/usr/bin/scriptreplay
/usr/bin/setarch
/usr/bin/setsid
/usr/bin/setterm
/usr/bin/tailf
/usr/bin/taskset
/usr/bin/time
/usr/bin/ul
/usr/bin/uname26
/usr/bin/unshare
/usr/bin/uuidgen
/usr/bin/wall
/usr/bin/whereis
/usr/bin/which
/usr/bin/write
/usr/bin/x86_64
/usr/sbin/addpart
/usr/sbin/delpart
/usr/sbin/fdformat
/usr/sbin/flushb
/usr/sbin/freeramdisk
/usr/sbin/klogconsole
/usr/sbin/ldattach
/usr/sbin/partx
/usr/sbin/rcraw
/usr/sbin/readprofile
/usr/sbin/rtcwake
/usr/sbin/setctsid
/usr/sbin/tunelp

util-linuxはバージョン2.19です。リリースノートは、日付が付けられたv2.13(2007年8月28日)まで簡単に見つけることができます。これのポイントや目標が何であるかわからない、それは確かに331回支持されたその長いことで答えられなかった。


2
質問がどのUnixを指しているかについて言及していないことに注意してください。Linuxはほんの数例の1つです。
クサラナンダ

2
あなたがwhich -v示すように、それはGNUであり(他の回答で言及された贅沢なものであり、Linuxに特有のものではありません)、whichユーティリティが含まれていないutil-linuxではありません。util-linux 2.19は2011年、GNUは2.19が2008年のものです。
StéphaneChazelas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.