自作スクリプトが「command not found」になる - PATHと実行権限
この記事で解決できること
- 自作スクリプトだけ
command not foundになる 理由 が分かる command not foundとPermission deniedを 正しく見分けられる./実行・chmod +x・PATH 追加・hash -rを 順に切り分け できる
結論(切り分けの型)
command not found は「PATH 上にそのコマンドが見つからない」サイン。自作スクリプトでは次の順で潰す。
- カレントで動かすなら
./script.sh(裸の名前は PATH しか探さない) - 実行権限を付ける
chmod +x script.sh - 常用するなら PATH に登録(
~/binや~/.local/bin) - 移動・入替後に古い場所を掴む ときは
hash -r
前提(対象環境)
- シェル: bash(Ubuntu / Debian 系の既定)
- 対象: 自分で書いた
.shスクリプトや自前ビルドのコマンド - zsh でも考え方は同じ。
hash -r等の細部のみ差がある
なぜ自作スクリプトは command not found になるのか?
結論: bash は裸のコマンド名を PATH 上のディレクトリだけから探す。カレントディレクトリは既定で PATH に含まれないため、
script.shと打っても見つからない。
ターミナルで script.sh と入力したとき、bash は次の順で実体を探す。
- シェル関数・エイリアス・ビルトイン
hashにキャッシュ済みのパス- 環境変数
PATHに列挙されたディレクトリ(先頭から順に)
つまり PATH のどこにも script.sh が無ければ、たとえ目の前のディレクトリにファイルがあっても command not found になる。
$ ls script.sh $ script.sh bash: script.sh: command not found
カレントディレクトリ(.)が PATH に入っていないのは 既定の安全設計 である。. を PATH 先頭に入れると、ls などの正規コマンドと同名の悪意あるファイルをカレントに置かれたとき、それを誤実行してしまう。だから「いま居る場所のスクリプト」は明示的に指定する必要がある。
command not found と Permission denied はどう違うのか?
結論:
command not foundは「見つからない」、Permission deniedは「見つかったが実行権限が無い」。出るメッセージで切り分けの方向が変わる。
この 2 つは原因が別物で、対処も異なる。
| メッセージ | 意味 | 主な対処 |
|---|---|---|
command not found |
PATH 上に該当コマンドが無い | ./ を付ける / PATH に登録 |
Permission denied |
ファイルはあるが実行ビットが無い | chmod +x |
bad interpreter |
shebang のパス誤り / CRLF 改行 | shebang 修正 / dos2unix |
# パスは合っているが実行権限が無い場合 $ ./script.sh bash: ./script.sh: Permission denied
./ を付けて初めて Permission denied に変わったなら、PATH 問題は解消し、次は権限の問題だと分かる。bad interpreter が出る場合は shebang か改行コードが原因なので、「bad interpreter」エラーの直し方を参照。
まず ./ を付けて実行する
結論: カレントにあるスクリプトは
./script.shで実行する。./はカレントディレクトリを明示するパス指定で、PATH 検索を経由しない。
# NG: PATH しか探さないので見つからない $ script.sh bash: script.sh: command not found # OK: カレントのファイルを直接指定 $ ./script.sh Hello
別ディレクトリにあるなら絶対パスや相対パスで指定してもよい。
$ /home/alice/tools/script.sh $ ~/tools/script.sh $ bash script.sh # bash に渡せば実行ビット無しでも動く
bash script.sh のようにインタプリタへ明示的に渡す方法なら、実行権限が無くても動く。「PATH 問題」と「権限問題」を一時的に切り分けたいときに有効。
実行権限が無いとどうなるのか?
結論: 実行ビットが無いと
./script.shはPermission deniedで止まる。chmod +xで実行権限を付与する。
新規作成・ダウンロード・別環境からコピーしたスクリプトは、実行権限が落ちていることが多い。
$ ls -l script.sh -rw-r--r-- 1 alice alice 42 Jun 6 10:00 script.sh $ ./script.sh bash: ./script.sh: Permission denied
-rw-r--r-- には x(実行ビット)が無い。付与する。
$ chmod +x script.sh $ ls -l script.sh -rwxr-xr-x 1 alice alice 42 Jun 6 10:00 script.sh $ ./script.sh Hello
chmod 777 は不要かつ危険。実行権限が欲しいだけなら chmod +x(または chmod 755)で十分。誰でも書き込み可能にする 777 はセキュリティリスクになる。
PATH に自分のディレクトリを登録するには?
結論: 毎回
./を打ちたくないなら、スクリプト置き場をPATHに追加する。~/.bashrcにexport PATHを書けば恒久化できる。
まず現在の PATH を確認する。
$ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
スクリプトを置くディレクトリ(例: ~/bin)を PATH に追加する。
# その場限り(現在のシェルのみ有効) $ export PATH="$HOME/bin:$PATH"
恒久化するには ~/.bashrc(ログインシェルでは ~/.profile)に追記する。
# ~/.bashrc の末尾に追加 export PATH="$HOME/bin:$PATH"
# 追記後、現在のシェルに反映 $ source ~/.bashrc $ which script.sh /home/alice/bin/script.sh $ script.sh # ./ 無しで実行できる Hello
~/bin と ~/.local/bin
Ubuntu の既定 ~/.profile は、ログイン時に ~/bin と ~/.local/bin が存在すれば自動で PATH に追加する。この 2 つにスクリプトを置けば、設定追記なしで PATH が通ることが多い(反映には再ログインが必要な場合がある)。
PATH の区切りは :、追加位置で優先順が決まる。$HOME/bin:$PATH なら自分のスクリプトが優先、$PATH:$HOME/bin なら既存コマンドが優先。同名コマンドの上書き事故を避けたいなら末尾に足すのが無難。
それでも見つからない・古い実体を掴むときは?
結論: bash は一度見つけたコマンドのパスを
hashにキャッシュする。スクリプトを移動・入れ替えた直後は古いパスを掴むことがある。hash -rでキャッシュを消す。
PATH も権限も正しいのに挙動がおかしいときは、type で「bash が何をどこから実行しようとしているか」を確認する。
$ type -a script.sh script.sh is /home/alice/bin/script.sh
type の結果が古い場所を指している、あるいは消したはずのコマンドがまだ動く場合はキャッシュが残っている。
# コマンドパスのキャッシュを消去 $ hash -r # 再確認 $ type -a script.sh
which はファイルシステム上の PATH を実際に検索するが、type はエイリアス・関数・ビルトイン・hash キャッシュまで含めて「bash が実際にどう解決するか」を示す。原因切り分けには type -a が確実。