自作スクリプトが「command not found」になる - PATHと実行権限

自作スクリプトが「command not found」になる - PATHと実行権限

この記事で解決できること

  • 自作スクリプトだけ command not found になる 理由 が分かる
  • command not foundPermission denied正しく見分けられる
  • ./ 実行・chmod +x・PATH 追加・hash -r順に切り分け できる

結論(切り分けの型)

command not found は「PATH 上にそのコマンドが見つからない」サイン。自作スクリプトでは次の順で潰す。

  1. カレントで動かすなら ./script.sh(裸の名前は PATH しか探さない)
  2. 実行権限を付ける chmod +x script.sh
  3. 常用するなら PATH に登録~/bin~/.local/bin
  4. 移動・入替後に古い場所を掴む ときは hash -r

前提(対象環境)

  • シェル: bash(Ubuntu / Debian 系の既定)
  • 対象: 自分で書いた .sh スクリプトや自前ビルドのコマンド
  • zsh でも考え方は同じ。hash -r 等の細部のみ差がある

なぜ自作スクリプトは command not found になるのか?

結論: bash は裸のコマンド名を PATH 上のディレクトリだけから探す。カレントディレクトリは既定で PATH に含まれないため、script.sh と打っても見つからない。

ターミナルで script.sh と入力したとき、bash は次の順で実体を探す。

  1. シェル関数・エイリアス・ビルトイン
  2. hash にキャッシュ済みのパス
  3. 環境変数 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.shPermission 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 に追加する。~/.bashrcexport 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 が確実。

チェックリストまとめ

結論: 「./ で実行 → chmod +x → PATH 登録 → hash -r」の順にたどれば、自作スクリプトの command not found はほぼ解決する。

上から順に確認する。

  • [ ] カレントのスクリプトは ./script.sh で実行している
  • [ ] ls -l で実行ビット(x)があり、無ければ chmod +x
  • [ ] Permission denied なら PATH ではなく権限の問題
  • [ ] 常用するなら ~/bin / ~/.local/bin に置くか export PATH~/.bashrc に追記
  • [ ] echo $PATH に置き場が含まれているか確認
  • [ ] 移動・入替後に挙動が変なら hash -r でキャッシュ消去
  • [ ] type -a script.sh で実際の解決先を確認

次に読む記事: