🙄

【command not foundとの出会い】Linuxで「PATHを通す」をちゃんと理解する

2024/03/17に公開

0.はじめに

Linuxで「command not found」と初めて遭遇した時は、「やっちまったか?!」と大いに焦りました。

でも、調べてみると原因はだいたい以下2つだということがわかりました。

  • コマンドの実行ファイルが存在しない
  • コマンドの実行ファイルまでのPATHが通っていない

「一体どういうことなんだ?!」という初学者の方向けに、「コマンドが使えない時は何をしたらいいのか?」「PATHを通すとは?そもそもPATHとは?」を噛み砕いて解説いたします。

1.コマンドはコマンド用のプログラムが叩かれて実行される

そもそもコマンドとは、コマンド用のプログラムのことです。

そして、OS(カーネル)はユーザーからの命令(コマンド)を受け取ったら、該当コマンド用のプログラムを実行し、実行結果を返します。

例えば、ディレクトリを移動するcdコマンドの実態は/usr/bin/cdに存在しており、OSはこいつを実行します。

cd: shell built-in command ← cdってコマンドは元々からあるコマンドだよ
/usr/bin/cd ← cdってコマンドは「/usr/bin/cd」にあるよ。てか、こいつだよ。

まずは、この「コマンドはコンピュータ上にあるコマンド用のプログラムが叩かれて実行される」を理解しましょう。

2.PATHはコマンドの場所を格納した環境変数

では、本題のPATHとは何なんでしょうか?

結論から言うと「PATHはコマンドの場所を格納した環境変数」のことです。

「何を言ってるんだ?低評価つけるぞ」って感じだと思いますので、「環境変数」「PATH変数」の順番に解説いたします。

2.1.環境変数とは

環境変数とは、ざっくり言うとOS上で動くプロセスが共有する情報を指します。

環境変数を設定しておけば、すべてのプロセスで共通で使用できるため、プロセスごとに変数を定義して値を設定する手間が省けます。

<イメージ図:環境変数はOSが持っている、みんなで共有できる変数>

例えば、echo $SHELLをterminalで実行すると、利用しているシェルの種類がわかります。

$ echo $SHELL ← コマンド実行
/bin/zsh ← 実行結果。zshというシェルを利用していることがわかる

また、exportというコマンドで新しい環境変数を一時的に定義できます。

$ export AGE=20 ← コマンド実行。AGEという箱を作り、そこに20という変数を定義
$ echo $AGE ← コマンド実行。AGEの中身を出力
20 ← AGEに定義した20という変数が出力

環境変数は理解が難しいのですが、まずは「OSが持っている変数なんだなぁ〜」と覚えていただければ十分です。

2.2.PATH変数とは

前述したとおり、OSはコマンドが実行されると、そのコマンドを探しに行きます。

例えば cd コマンドを実行したら、OSは「”cd”というコマンドが打たれた、cdはどこっ?!」とコンピュータ内で「cdのプログラム(/usr/bin/cd)」を探しに行きます。

でも、コンピュータの中にはたくさんのファイルやフォルダがあるので、コンピュータ内全てを探してたらかなり時間がかかりますよね?

そこで $PATH 変数の登場。$PATH変数に登録しているパスだけ探す」のです。そうすることで、コンピュータ全部を探さなくてすみます。

「command not found」とはつまり「環境変数の$PATHに指定しているパス配下をみてみたけどXXXなんてプログラムはなかったよ!」とOSは言っているわけです。

実際に$PATH変数をみてみましょう。

$ echo $PATH
/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin

パスが:で区切られているのがわかります。つまり、:で区切られたこれらのパス配下のプログラムのみを、コマンドで実行された時にそのプログラムを探しに行きます。

3.「command not found」がでたら確認すること

さて、なんとなく「PATHを通すとは?そもそもPATHとは?」はわかっていただけたのではないでしょうか?

ここからは、「command not found」が出た際の具体的な対処法を解説します。

  • コマンドの実行ファイルの有無(場所)を確認する
  • 実行ファイルの場所にPATHが通ってるか確認する

3.1.コマンドの実行ファイルの有無(場所)を確認する

そもそも、コマンドの実行ファイルがあるのか確認しましょう。

ない場合は、アプリケーションのダウンロードを、ある場合は次の手順に利用するので実行結果(パス)をメモっておきましょう。

WhichコマンドでPATH環境変数にリストされたディレクトリを検索し確認ができます。

<ある場合>

$ which -a cd
cd: shell built-in command
/usr/bin/cd

<ない場合>

$ which -a cccc
cccc not found

3.2.実行ファイルの場所にPATHが通ってるか確認する

これは前述した通り、echo $PATHで確認ができます。

4.「command not found」がでたらやること

最後に、「command not found」を解消する方法を述べます。

具体的には以下3つです。結論から言うと、ほとんどの場合は③を実施することになります(というかそれが無難)。

  • ①プログラムが入っているところにPATHを通す
  • ②すでにPATHが通っているパスにプログラムを移動する
  • ③すでにPATHが通っているところにシンボリックリンクを貼る

4.1.プログラムが入っているところにPATHを通す

プログラムが入っているところにPATHを通すというやり方です。つまり、$PATH変数に新しいプログラムの場所を追加するわけです。

ただ通常は、単一のアプリケーションのために、PATHを追加することはしません。それをいちいち $PATHに追加していたら大変めんどくさいからです。

そのため、この方法を実行するのは大量のプログラムがそのパスに入っている場合です。

4.2.すでにPATHが通っているパスにプログラムを移動する

これは、例えば/usr/binなどすでにPATHが通っているフォルダへプログラムを移動させるという方法です。

「必ずTerminalから起動するもの」であればそれでもいいかもしれませんが、GUIから起動することもあるアプリケーションであれば、/Applications/に入れておくのがベターです。

なので、あまりこの方法も単一アプリケーションのために実施することは少ないです(ほぼない)。

4.3.すでにPATHが通っているところにシンボリックリンクを貼る

PATHをわざわざ通すほどでもないけど、プログラムも移動させたくない(①も②もやりたくない)ケースでは、「すでにPATHが通っているところにシンボリックリンクを貼る」ということで解決させます。

シンボリックリンクというのは、物理的にはコピーはしないけど、参照先としてリンクを貼るものです。

例えば「/usr/local/binの配下にAAAというものはないけど、/usr/local/bin/AAAを求められたら /Applications/AAA.app/Contents/SharedSupport/bin/AAAを参照してね」という風に、参照先を指定することができます。

こうすることで、物理的にファイルを動かすことなく、PATHを通すことができます。これはよく使う技なので覚えておきましょう。

たとえば、ディレクトリ「test/sub」に存在するtest.txtのリンクを、ディレクトリ「test」からシンボリックリンクttを作成するには以下のように実施します。

$ ln -s sub/test.txt tt
$ ls

5.おわりに

かなり長くなりましたが、まとめると、、、

  • OSはPATHが通っているところしかコマンドを探しに行かない
  • PATHが通っているところ=環境変数の$PATH変数に指定されているパス
  • 大抵の場合は、$PATHをいじるのではなくて、シンボリックリンクを貼ることで解決する
  • $ls -s <target_path> <link_path>で、シンボリックリンクを貼る

初学者の方にとってはむずかしいと思います。

ただ、何も理解しないでPATHをいじるとすべてのコマンドが使えなくなる恐れもあるので、きちんと理解できたほうがいいですね。

お疲れまでしたっ!

Discussion