- 技術 💻
whichコマンドの使い方|whereis・type・command -vの違い
whichは、コマンド名を渡すと「環境変数PATHの中で最初に見つかった実行ファイルのフルパス」を表示するコマンドです。 which python3 と打てば /usr/bin/python3 のように場所がわかります。似た役割のコマンドに whereis・type・command -v があり、「何を探すか」と「エイリアスや関数を知っているか」が違います。
この記事では、同じコマンド名に対して which / whereis / type / command -v の4つを実行し、その出力を並べて比べます。エイリアス・関数・シェル組み込み・PATHの外にあるファイル、それぞれの場合に何が返るかを早見表にまとめ、whichがエイリアスを見落として、実際に動くものとは別の実体を指す例も再現しました。シェルスクリプトで「コマンドがあるか」を確かめる command -v の定型文も、コピーして使える形で載せています。出力は Ubuntu 24.04.4 LTS(bash 5.2.21)で実行したものです。RHEL系の which については手元で動かせなかったため、その節に未実行と明記しています。
which・whereis・type・command -v の早見表
まず、4つの違いを1つの表にまとめます。
| 項目 | which | whereis | type | command -v |
|---|---|---|---|---|
| 正体 | 外部コマンド(Ubuntuではシェルスクリプト) | 外部コマンド(util-linux) | シェルの組み込み | シェルの組み込み |
| 探す場所 | PATH | あらかじめ決まった場所(バイナリ・マニュアル・ソース) | シェルが実際に実行する順 | シェルが実際に実行する順 |
| エイリアスがわかる | わからない(Ubuntu) | わからない | わかる | わかる |
| 関数がわかる | わからない | わからない | わかる(中身も表示) | わかる(名前だけ表示) |
| シェル組み込みがわかる | わからない | わからない | わかる | わかる(名前だけ表示) |
| 見つからないときの終了ステータス | 1 | 0 | 1 | 1 |
| 出力の形 | パスだけ | 「名前: パス…」 | 人が読む文章 | パスまたは名前だけ |
| 向いている用途 | 実行ファイルの場所をさっと知る | マニュアルや関連ファイルの場所も知る | 対話的に「これは何か」を調べる | スクリプトでの存在確認 |
目的から選ぶなら、次の表が近道です。
| やりたいこと | 使うコマンド |
|---|---|
| 実行ファイルのフルパスを知りたい | which コマンド名 |
| PATH上の同名ファイルをすべて知りたい | which -a コマンド名 |
| 打ったときに実際に何が動くかを知りたい | type コマンド名 |
| エイリアス・関数・ファイルを全部並べたい | type -a コマンド名 |
| 種類だけを1語で知りたい | type -t コマンド名 |
| スクリプトで存在を確かめたい | command -v コマンド名 >/dev/null 2>&1 |
| マニュアルや設定の場所も知りたい | whereis コマンド名 |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「whichコマンドの使い方|whereis・type・command -vの違い」
検証環境と準備
| 項目 | 内容 |
|---|---|
| OS | Ubuntu 24.04.4 LTS |
| シェル | GNU bash 5.2.21(対話モード)。比較用に dash と zsh 5.9 |
| which | debianutils 5.17 の /usr/bin/which(実体は which.debianutils) |
| whereis | util-linux 2.39.3 |
| ホームディレクトリ | /tmp/t199home(検証用。ふだんは /home/ユーザー名) |
| PATH | /tmp/t199home/bin0:/tmp/t199home/bin1:/usr/local/bin:/usr/bin:/bin |
比べるために、エイリアス2つと関数1つを定義しました。
alias ls='ls --color=auto'
alias ll='ls -l'
greet() { echo "こんにちは"; }
このほかに、PATHの外に置いたスクリプト ~/tools/hello.sh と、存在しない名前 nosuchcmd を使います。
whichの基本的な使い方
コマンドの場所を調べる
which python3
実行結果:
/usr/bin/python3
whichは、PATHに並んだディレクトリを左から順に見ていき、最初に見つかった実行ファイルのフルパスを1行表示します。
同じ名前のコマンドをすべて表示する(-a)
-a を付けると、PATH上で見つかったものをすべて表示します。引数は複数並べられます。
$ which -a echo python3
/usr/bin/echo
/bin/echo
/usr/bin/python3
/bin/python3
手元の環境では /bin と /usr/bin の両方がPATHに入っているため、同じコマンドが2行ずつ表示されました。上に表示されたものが、名前だけで実行したときに使われる候補です。
見つからないときは何も表示せず、終了ステータスが1になる
$ which nosuchcmd; echo "status=$?"
status=1
Ubuntuの which は、見つからないときにメッセージを出しません。終了ステータス($?)が 1 になることで、見つからなかったとわかります。複数の名前を渡したときは、1つでも見つからなければ 1 になります。
$ which ls cd python3; echo "status=$?"
/usr/bin/ls
/usr/bin/python3
status=1
cd はシェルの組み込みコマンドで、実行ファイルがPATH上にないため表示されていません。
Ubuntuの which のオプション
Ubuntu(Debian系)の which は、シェルスクリプトで書かれた小さなコマンドです。
$ file -L /usr/bin/which
/usr/bin/which: POSIX shell script, ASCII text executable
$ which --version
Illegal option --
Usage: /usr/bin/which [-as] args
使えるオプションは -a と -s の2つで、--version や --help はありません。
| オプション | 意味 | 確認 |
|---|---|---|
| (なし) | 最初に見つかった1つを表示 | 実行して確認 |
-a |
見つかったものをすべて表示 | 実行して確認 |
-s |
何も表示せず、終了ステータスだけ返す | 実行して確認(which -s ls は status=0) |
フルパスを渡すこともできる
PATHの外にあるファイルでも、パスを付けて渡せば、実行できるファイルかどうかを確かめられます。
$ which ~/tools/hello.sh
/tmp/t199home/tools/hello.sh
同じ名前に4つのコマンドを実行して比べる
ここからがこの記事の中心です。対象ごとに4つのコマンドを実行し、出力を並べます。
ケース1:エイリアスと実行ファイルの両方がある(ls)
ls は /usr/bin/ls という実行ファイルがあり、さらに alias ls='ls --color=auto' も定義されています。
$ which ls; echo "status=$?"
/usr/bin/ls
status=0
$ whereis ls
ls: /usr/bin/ls
$ type ls
ls is aliased to `ls --color=auto'
$ command -v ls
alias ls='ls --color=auto'
which と whereis は実行ファイルの場所を、type と command -v はエイリアスであることを返しました。 ターミナルで ls と打ったときに実際に動くのはエイリアスのほうなので、「打ったら何が動くか」を正しく答えているのは type と command -v です。
ケース2:エイリアスだけがある(ll)
$ which ll; echo "status=$?"
status=1
$ whereis ll
ll:
$ type ll
ll is aliased to `ls -l'
$ command -v ll
alias ll='ls -l'
ll という実行ファイルはないので、which は何も表示せず status=1、whereis は名前だけを表示しました。「which で見つからない=使えない」とは限らないことがわかります。
ケース3:シェル関数(greet)
$ which greet; echo "status=$?"
status=1
$ whereis greet
greet:
$ type greet
greet is a function
greet ()
{
echo "こんにちは"
}
$ command -v greet
greet
type は関数の中身まで表示します。command -v は関数名だけを返します。
ケース4:シェル組み込み(cd)
$ which cd; echo "status=$?"
status=1
$ whereis cd
cd:
$ type cd
cd is a shell builtin
$ command -v cd
cd
cd はシェルの中に組み込まれたコマンドで、ファイルとしては存在しません。
ケース5:組み込みと実行ファイルの両方がある(echo)
$ which echo
/usr/bin/echo
$ whereis echo
echo: /usr/bin/echo
$ type echo
echo is a shell builtin
$ command -v echo
echo
$ type -a echo
echo is a shell builtin
echo is /usr/bin/echo
echo is /bin/echo
echo には、シェル組み込み版と /usr/bin/echo の両方があります。名前だけで実行すると組み込み版が使われますが、which は外部コマンドなのでシェルの中身を知らず、ファイルのほうを答えます。type -a を使うと、優先される順に全部が並びます。
ケース6:シェルのキーワード(if)
$ which if; echo "status=$?"
status=1
$ type if
if is a shell keyword
$ command -v if
if
ケース7:ふつうの実行ファイル(python3)
$ which python3
/usr/bin/python3
$ whereis python3
python3: /usr/bin/python3 /usr/lib/python3 /etc/python3 /usr/share/python3
$ type python3
python3 is /usr/bin/python3
$ command -v python3
/usr/bin/python3
エイリアスも関数もないふつうのコマンドでは、which・type・command -v は同じパスを指します。whereis だけは、実行ファイルに加えて同じ名前の関連ディレクトリも並べています。
ケース8:PATHの外にあるファイル(hello.sh)
~/tools/hello.sh は実行できるスクリプトですが、~/tools はPATHに入っていません。
$ which hello.sh; echo "status=$?"
status=1
$ whereis hello.sh
hello.sh:
$ type hello.sh; echo "status=$?"
bash: type: hello.sh: not found
status=1
$ command -v hello.sh; echo "status=$?"
status=1
4つとも見つけられません。4つのコマンドはどれも「ディスク全体からファイルを探す」ものではないためです。パスを付けて渡すと、次のように答えが返ります。
$ command -v ~/tools/hello.sh
/tmp/t199home/tools/hello.sh
$ type ~/tools/hello.sh
/tmp/t199home/tools/hello.sh is /tmp/t199home/tools/hello.sh
場所がわからないファイルを探すときは、find コマンドなど別の道具を使います。
ケース9:存在しないコマンド(nosuchcmd)
$ which nosuchcmd; echo "status=$?"
status=1
$ whereis nosuchcmd; echo "status=$?"
nosuchcmd:
status=0
$ type nosuchcmd; echo "status=$?"
bash: type: nosuchcmd: not found
status=1
$ command -v nosuchcmd; echo "status=$?"
status=1
whereis だけは、見つからなくても終了ステータスが 0 でした。スクリプトの存在確認に whereis の終了ステータスを使うと、常に「ある」と判定されてしまうので、この用途には command -v が向いています。
ケース別の早見表(何が返るか)
9つのケースの結果を1つの表にまとめます(bash 5.2.21・Ubuntu 24.04 での実行結果)。「―」は何も表示されなかったことを表します。
| 対象 | which | whereis | type | command -v |
|---|---|---|---|---|
| ls(エイリアス+ファイル) | /usr/bin/ls | ls: /usr/bin/ls | エイリアスと表示 | alias ls='ls --color=auto' |
| ll(エイリアスのみ) | ―(status 1) | ll: | エイリアスと表示 | alias ll='ls -l' |
| greet(関数) | ―(status 1) | greet: | 関数と表示(中身つき) | greet |
| cd(組み込み) | ―(status 1) | cd: | cd is a shell builtin | cd |
| echo(組み込み+ファイル) | /usr/bin/echo | echo: /usr/bin/echo | echo is a shell builtin | echo |
| if(キーワード) | ―(status 1) | if: | if is a shell keyword | if |
| python3(ファイル) | /usr/bin/python3 | 実行ファイルと関連ディレクトリ | python3 is /usr/bin/python3 | /usr/bin/python3 |
| hello.sh(PATH外) | ―(status 1) | hello.sh: | not found(status 1) | ―(status 1) |
| nosuchcmd(存在しない) | ―(status 1) | nosuchcmd:(status 0) | not found(status 1) | ―(status 1) |
この表から読み取れることは3つです。
- which と whereis はファイルしか見ない。 エイリアス・関数・組み込みは対象外です。
- type と command -v はシェルの目線で答える。 打ったときに実際に動くものがわかります。
- 終了ステータスで存在を判定できるのは which・type・command -v。 whereis は常に 0 でした。
シェルがコマンドを探す順番
なぜ which の答えと実際に動くものがずれるのかは、シェルがコマンド名を解決する順番を見るとわかります。bashでは、おおよそ次の順で探します。
graph TD
A["コマンド名を入力"] --> B{"エイリアスか"}
B -->|"はい"| B1["エイリアスを展開"]
B -->|"いいえ"| C{"キーワードか"}
C -->|"はい"| C1["シェルの構文として処理"]
C -->|"いいえ"| D{"関数か"}
D -->|"はい"| D1["関数を実行"]
D -->|"いいえ"| E{"組み込みか"}
E -->|"はい"| E1["組み込みを実行"]
E -->|"いいえ"| F["PATHを左から探して実行"]
| 順番 | 種類 | type -t の表示 | which が知っているか |
|---|---|---|---|
| 1 | エイリアス | alias | 知らない |
| 2 | キーワード(if、for など) | keyword | 知らない |
| 3 | 関数 | function | 知らない |
| 4 | 組み込み(cd、echo など) | builtin | 知らない |
| 5 | PATH上のファイル | file | 知っている |
which は別のプロセスとして動く外部コマンドなので、いまのシェルの中にだけあるエイリアスや関数を見ることができません。 見えるのは 5 のファイルだけです。type -t を使うと、種類を1語で確かめられます。
$ type -t ls ll greet cd if python3
alias
alias
function
builtin
keyword
file
whichがエイリアスを見落として別の実体を指す再現例
「which で確かめたパスと、実際に動いているものが違う」という状況を再現します。同じ名前のスクリプト mytool を2か所に置きました。
| 場所 | 内容 | PATHに入っているか |
|---|---|---|
| ~/bin1/mytool | 「mytool v1」と表示する | 入っている |
| ~/bin2/mytool | 「mytool v2」と表示する | 入っていない |
まずはそのまま実行します。
$ mytool
mytool v1
$ which mytool
/tmp/t199home/bin1/mytool
ここで、v2 を指すエイリアスを定義します。
$ alias mytool="$HOME/bin2/mytool"
$ mytool
mytool v2
$ which mytool
/tmp/t199home/bin1/mytool
実際に動いているのは v2(bin2)なのに、which は v1(bin1)のパスを答えています。 which の結果だけを見て「bin1 のファイルを直せばよい」と判断すると、直したのに動きが変わらない、ということが起こります。type で確かめると、正しい答えが得られます。
$ type mytool
mytool is aliased to `/tmp/t199home/bin2/mytool'
$ type -a mytool
mytool is aliased to `/tmp/t199home/bin2/mytool'
mytool is /tmp/t199home/bin1/mytool
$ command -v mytool
alias mytool='/tmp/t199home/bin2/mytool'
type -a は、優先される順(エイリアス → ファイル)にすべてを表示します。「思っていたものと違うものが動いている」と感じたら、まず type -a コマンド名 を実行するのがおすすめです。
エイリアスを通さずに実行したいときは、名前の前に \ を付けるか、command を付けます。
$ \mytool
mytool v1
$ command mytool
mytool v1
関数でも同じことが起こる
$ python3() { echo "これは関数です"; }
$ which python3
/usr/bin/python3
$ type python3
python3 is a function
python3 ()
{
echo "これは関数です"
}
$ python3
これは関数です
関数を取り消すには unset -f python3、エイリアスを取り消すには unalias 名前 を使います。
ハッシュ(場所の記憶)でずれる例
bashは、一度実行したコマンドの場所を覚えておき、次からはPATHを探し直さずにその場所を使います。この記憶をハッシュと呼びます。PATHの手前のディレクトリ(~/bin0)に、あとから同じ名前のファイルを置くとどうなるかを確かめました。
$ mytool
mytool v1
$ type mytool
mytool is hashed (/tmp/t199home/bin1/mytool)
ここで ~/bin0/mytool(「mytool v0」と表示する)を新しく置きます。
$ which mytool
/tmp/t199home/bin0/mytool
$ mytool
mytool v1
$ type mytool
mytool is hashed (/tmp/t199home/bin1/mytool)
$ hash -r
$ mytool
mytool v0
$ type mytool
mytool is hashed (/tmp/t199home/bin0/mytool)
which は毎回PATHを探すので新しい bin0 を答えますが、bashは覚えていた bin1 を実行し続けました。 hash -r で記憶を消すと、bin0 が実行されるようになります。コマンドを入れ直した直後に動きが変わらないときは、hash -r を実行するか、新しいターミナルを開くと解決します。
| ずれの原因 | which の答え | 実際に動くもの | 確かめ方 | 直し方 |
|---|---|---|---|---|
| エイリアス | PATH上のファイル | エイリアスの内容 | type -a 名前 |
unalias 名前、または \名前 で実行 |
| 関数 | PATH上のファイル | 関数 | type 名前 |
unset -f 名前 |
| ハッシュ | いまのPATHで最初のファイル | 以前に覚えた場所 | type 名前(hashed と出る) |
hash -r |
シェルスクリプトで存在確認に command -v を使う定型文
スクリプトの中で「このコマンドが入っているか」を確かめるときは、command -v が定番です。シェルの組み込みなので追加のインストールが要らず、sh でも bash でも同じ書き方で動き、見つからないときは終了ステータスが 0 以外になります。
定型文1:必要なコマンドをまとめて確認する(sh でも動く)
#!/bin/sh
# 必要なコマンドがそろっているかを最初に確認する
for cmd in git curl nosuchcmd; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "エラー: $cmd が見つかりません。インストールしてから再実行してください。" >&2
missing=1
fi
done
[ -z "$missing" ] || exit 1
echo "必要なコマンドはそろっています"
実行結果(sh と dash の両方で同じ結果でした):
$ sh check.sh
エラー: nosuchcmd が見つかりません。インストールしてから再実行してください。
status=1
>/dev/null 2>&1 は、見つかったときのパス表示とエラーメッセージの両方を捨て、終了ステータスだけを使うための書き方です。
定型文2:ある・なしで処理を分ける/関数にまとめる
#!/bin/bash
if command -v python3 >/dev/null 2>&1; then
echo "python3 あり: $(command -v python3)"
else
echo "python3 なし"
fi
command -v nosuchcmd >/dev/null 2>&1 || echo "nosuchcmd なし"
require() { command -v "$1" >/dev/null 2>&1 || { echo "$1 が必要です" >&2; exit 127; }; }
require tar
require nosuchcmd
echo "ここには来ません"
実行結果:
python3 あり: /usr/bin/python3
nosuchcmd なし
nosuchcmd が必要です
status=127
require 関数にしておくと、スクリプトの冒頭に require コマンド名 を並べるだけで済みます。
定型文3:候補の中から最初に見つかったものを使う
#!/bin/bash
PY=$(command -v python3.14 || command -v python3 || command -v python) || { echo "Python が見つかりません" >&2; exit 1; }
echo "使う Python: $PY"
実行結果:
使う Python: /usr/bin/python3
スクリプトで which より command -v が選ばれる理由
| 観点 | which | command -v |
|---|---|---|
| どこにでもあるか | 外部コマンド。最小構成の環境では入っていないこともある | シェルの組み込み。sh があれば使える |
| 見つからないときの出力 | 実装によって違う(Ubuntuは何も出さない) | 何も出さない |
| 終了ステータス | 実装によって扱いが違うことがある | 見つかれば 0、なければ 0 以外 |
| 関数や組み込みを使う場合 | 見つけられない | 見つけられる |
dash(Ubuntu の sh)での command -v の結果も確かめました。見つからないときの終了ステータスは 127 でした。
[ls]
/usr/bin/ls
command -v: 0
[ll]
alias ll='ls -l'
command -v: 0
[greet]
greet
command -v: 0
[cd]
cd
command -v: 0
[nosuchcmd]
command -v: 127
bash では 1、dash では 127 と値は違いますが、どちらも「0 以外」です。判定は if command -v ...; then の形にして、0 かどうかだけを見ると、シェルの違いに左右されません。
スクリプトの中ではエイリアスは使われない
対話的でない bash(スクリプトや bash -c)では、既定でエイリアスが展開されません。次のように、エイリアスを定義しても type はファイルを答えます。
$ bash -c 'alias ls="ls -l"; type ls; command -v ls'
ls is /usr/bin/ls
/usr/bin/ls
そのため、スクリプトの中の command -v は、ほとんどの場合パスまたは関数名を返します。
実行権限のないファイルの扱い(手元での観察)
PATH上に、実行権限のない同名ファイルだけがある場合の結果です。
which=1
/tmp/t199home/bin1/mytool
command -v=0
which は何も表示せず 1 を返し、bash 5.2.21 の command -v はパスを表示して 0 を返しました。まれな状況ですが、権限まで確かめたいときは [ -x "$(command -v 名前)" ] のように -x の判定を足すと確実です。
type と whereis の便利なオプション
type のオプション(bash)
| 書き方 | 意味 | 実行結果の例 |
|---|---|---|
type 名前 |
何が動くかを文章で表示 | cd is a shell builtin |
type -a 名前 |
該当するものをすべて表示 | エイリアス、ファイルの順に並ぶ |
type -t 名前 |
種類だけを1語で表示 | alias / keyword / function / builtin / file |
type -P 名前 |
エイリアスなどを無視して、PATH上のパスを表示 | /usr/bin/ls |
type -p 名前 |
ファイルとして実行される場合だけパスを表示 | ls がエイリアスのときは何も表示されなかった |
$ type -a ls
ls is aliased to `ls --color=auto'
ls is /usr/bin/ls
ls is /bin/ls
$ type -P ls
/usr/bin/ls
command -V(大文字のV)は、type とほぼ同じ文章形式で表示します。
$ command -V ls
ls is aliased to `ls --color=auto'
$ command -V cd
cd is a shell builtin
$ command -V python3
python3 is /usr/bin/python3
whereis のオプション
whereis は、実行ファイル(バイナリ)・マニュアル・ソースの3種類を、あらかじめ決まった場所から探します。
| 書き方 | 意味 |
|---|---|
whereis 名前 |
バイナリ・マニュアル・ソースをまとめて表示 |
whereis -b 名前 |
バイナリだけを表示 |
whereis -m 名前 |
マニュアルだけを表示 |
whereis -s 名前 |
ソースだけを表示(手元では ls: とだけ表示) |
whereis -l |
探す場所の一覧を表示 |
$ whereis -b python3
python3: /usr/bin/python3 /usr/lib/python3 /etc/python3 /usr/share/python3
$ whereis -l | head -n 5
bin: /usr/bin
bin: /usr/sbin
bin: /usr/lib/x86_64-linux-gnu
bin: /usr/lib
bin: /usr/lib64
検証環境はマニュアルを省いた最小構成のため、whereis -m ls は ls: とだけ表示されました。マニュアルが入っている環境では、ここにマニュアルファイルのパスが並びます。
シンボリックリンクの先まで知りたいとき
which や command -v が返すパスは、シンボリックリンク(別のファイルを指す近道)であることがあります。本来のファイルは readlink -f で確かめられます。
$ ls -l /usr/bin/python3
lrwxrwxrwx 1 root root 25 May 8 16:24 /usr/bin/python3 -> /etc/alternatives/python3
$ readlink -f "$(command -v python3)"
/usr/bin/python3.11
Ubuntu系とRHEL系・zshで which の挙動が違う点
which は1つの決まった実装ではなく、環境によって中身が違います。エイリアスを表示するかどうかが、いちばん目立つ違いです。
| 環境 | which の正体 | エイリアスの表示 | 見つからないとき | この記事での確認 |
|---|---|---|---|---|
| Ubuntu・Debian系(bash) | debianutils のシェルスクリプト | 表示しない | 何も表示せず status 1 | 実行して確認 |
| RHEL系(bash) | GNU which。ログイン時の設定で which 自体がエイリアスになっている構成が一般的 | 表示する | 「no 名前 in (PATH)」の形のメッセージ | 未実行 |
| zsh | シェルの組み込み | 表示する | 「名前 not found」 | 実行して確認(zsh 5.9) |
Ubuntu(bash)での結果
$ which ls; echo "status=$?"
/usr/bin/ls
status=0
$ which ll; echo "status=$?"
status=1
エイリアスは表示されず、ファイルのパスだけが返ります。
RHEL系での挙動(未実行)
RHEL・AlmaLinux・Rocky Linux・Fedora などでは、GNU which が使われています。今回の検証環境にはこれらのOSを用意できなかったため、この節の内容は手元で実行しておらず、一般に知られている挙動としての説明です。
RHEL系では、ログイン時に読み込まれる設定で、which が次のような形のエイリアスになっている構成が広く知られています。いまのシェルのエイリアスと関数の一覧を、GNU which に標準入力で渡す仕組みです。
# RHEL系で一般に見られる定義の形(未実行・参考)
alias which='(alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot'
このため、which ls を実行すると、エイリアスの定義と実行ファイルのパスの両方が表示される、という挙動になります。ご自身のRHEL系環境で確かめるには、次の3つを実行して見比べるとわかりやすいです。
type which # which 自体がエイリアスかどうか
which ls # エイリアスの定義とパスが表示されるか
/usr/bin/which ls # エイリアスを通さない which の結果
同じ which ls でも、Ubuntuではパスだけ、RHEL系ではエイリアスの定義も、と結果が変わりえます。手順書やスクリプトで which の出力をそのまま使うと、環境によって結果が変わるため、どの環境でも同じ結果がほしい場面では command -v や type を使うと安定します。
zsh での結果(実行して確認)
zsh では which がシェルの組み込みで、エイリアス・関数・組み込みも表示します。
% which ll
ll: aliased to ls -l
status=0
% which greet
greet () {
echo hi
}
status=0
% which cd
cd: shell built-in command
status=0
% which ls
/usr/bin/ls
status=0
% which nosuchcmd
nosuchcmd not found
status=1
% /usr/bin/which ll
status=1
最後の行のように、zsh の中からでも外部コマンドの /usr/bin/which を直接呼ぶと、Ubuntuの which の挙動(何も表示せず status 1)になります。「which」と打ったときに何が動いているか自体を、type which で確かめられると覚えておくと便利です。
sudo を付けると PATH が変わることがある
sudo で実行するコマンドは、sudo の設定で決められた PATH で探されます。検証環境では次のとおりでした。
$ sudo sh -c 'echo $PATH'
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
ホーム配下のディレクトリ(~/bin など)は含まれていません。「ふつうに実行すると動くのに、sudo を付けると command not found になる」ときは、PATHの違いが原因であることが多いので、フルパスで指定すると解決します。
「見つからない」ときのチェックリスト
| 症状 | 確かめること | コマンド |
|---|---|---|
| which で何も表示されない | エイリアス・関数・組み込みではないか | type 名前 |
| which では見つかるのに違うものが動く | エイリアス・関数・ハッシュ | type -a 名前、hash -r |
| インストールしたのに見つからない | そのディレクトリがPATHに入っているか | echo $PATH |
| 入れ直したのに以前のものが動く | ハッシュが残っていないか | hash -r |
| sudo を付けると見つからない | sudo のPATHに含まれているか | フルパスで指定する |
| 実体のファイルを知りたい | シンボリックリンクではないか | readlink -f "$(command -v 名前)" |
| スクリプトで判定したい | 終了ステータスを使えているか | command -v 名前 >/dev/null 2>&1 |
よくある質問
Q. which と whereis の違いは何ですか? A. which はPATHの中から実行ファイルを探し、最初に見つかった1つのパスを表示します。whereis は決まった場所から、実行ファイルに加えてマニュアルやソースなどの関連ファイルも探して1行で表示します。実行ファイルの場所だけ知りたいなら which、関連ファイルも知りたいなら whereis が向いています。
Q. which と type はどちらを使えばよいですか?
A. ターミナルで「このコマンドを打つと何が動くか」を知りたいときは type がおすすめです。エイリアス・関数・組み込みまで含めて答えてくれます。実行ファイルのパスだけがほしいときは which や type -P が手軽です。
Q. シェルスクリプトでコマンドの存在確認をするには、どれを使いますか?
A. command -v 名前 >/dev/null 2>&1 が定番です。シェルの組み込みなので sh でも bash でも使え、見つからないときは終了ステータスが 0 以外になります。if command -v git >/dev/null 2>&1; then ... fi の形で使います。
Q. which で何も表示されないのに、コマンドは実行できます。なぜですか?
A. そのコマンドがエイリアス・関数・シェル組み込みのいずれかだからです。Ubuntuの which はPATH上のファイルだけを探すため、これらは表示されません。type 名前 を実行すると種類がわかります。
まとめ
- which はPATH上の実行ファイルのフルパスを表示します。
-aで同名のものをすべて表示できます。 - which と whereis はファイルだけを見る。 エイリアス・関数・組み込みは対象外です。
- type と command -v はシェルの目線で答える。 打ったときに実際に動くものがわかります。
- 「違うものが動いている」と感じたら
type -a 名前。 エイリアス・関数・ハッシュのどれが原因かがわかります。 - スクリプトの存在確認は
command -v 名前 >/dev/null 2>&1。 終了ステータスで判定します。 - whereis は見つからなくても終了ステータスが 0 なので、判定には使わず、関連ファイルの場所を知る用途に使います。
- which の挙動は環境で変わる。 Ubuntu(bash)はエイリアスを表示せず、zsh は表示します。RHEL系は表示する構成が一般的です(未実行)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「whichコマンドの使い方|whereis・type・command -vの違い」
株式会社RJCでは、whichコマンドやシェルがコマンドを探す仕組みを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。