はじめに
学びはじめのころ。
- なんかコマンドが認識されないが...?
- 先輩が言うとおりに、環境変数に何らかのパスを追加したら解消したが...?
- 仕組みが分かってないけど「解消したし、まあええか」で終了
となりがち(個人的見解)な、「パスを通す」の自分の理解度を確認するために説明してみます。
目次
- よくあるエラー
- なんで環境変数にパスを通すとコマンドが認識されるの?
- いろいろある環境変数
- コマンド実行の流れ
- コマンドには種類がある
- Linuxにおいて環境変数を設定するための方法
- コラム:外部コマンドのプログラムを確認してみる
- まとめ
- おわりに
よくあるエラー
git コマンド使いたいのに...。
$ git status
'git' is not recognized as an internal or external command,
operable program or batch file.
npm run buildしたいのに...。
$ npm run build
‘npm’ は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません
これらは環境変数にパスを通すとだいたい解決します。
なんで環境変数にパスを通すとコマンドが認識されるの?
なぜなら、シェルは環境変数 PATH
に登録されているディレクトリのパスからコマンドを探し出すから です。
いろいろある環境変数
今回のテーマである環境変数 PATH
以外にも、環境変数にはいくつかの種類が定義されている。現在設定されている環境変数は以下のように一覧表示できる。
$ env # 若干の違いがあるけど `printenv` コマンドでも表示可能
SHELL=/bin/bash
NAME=_mi
PWD=/usr/bin
LOGNAME=_mi
...以下略
シェルスクリプトで使いやすいものをピックアップすると、以下とかでしょうか?
環境変数 | 概要 |
---|---|
PATH |
シェルがコマンドを探すときの検索先ディレクトリリスト |
HOME |
現在のログインユーザーのホームディレクトリのパスを表す |
PWD |
現在の作業ディレクトリのパスを表す |
EDITOR |
使用するテキストエディタの名前を指定する |
USER |
現在のログインユーザー名を表す |
GROUP |
現在のログインユーザーが所属しているグループの名前を表す |
これ以外にもたくさんの環境変数がありますが、シェルがコマンドを探しだすためには PATH
への登録が必要なのです。
コマンド実行の流れ
コマンドは以下のような流れで実行される。
- ユーザーが端末(ターミナル)にコマンドを入力する
- 端末(ターミナル)がシェルに入力された文字列を渡す
- シェルは受け取った文字列(コマンド)を解析し、実行するべきコマンドを特定する
- シェルはコマンドを探すために環境変数
PATH
を探しに行く - シェルは
PATH
の中で最初にマッチしたコマンド(プログラム)を見つける - シェルがカーネルに対してコマンドの実行要求を出す
- カーネルはコマンドの実行ファイル(プログラム)を見つける
- カーネルがコマンドを実行するために、CPUやメモリなどを割り当てる
- カーネルがコマンドを実行する
- カーネルは実行結果を受け取り、デバイスや端末(ターミナル)に出力する
※ PATH
には複数のディレクトリがコロンで区切られて含まれているが、これはコマンドを探す順番が指定されている
なので、環境変数の PATH
にコマンドの実行可能ファイルが存在していない場合は『そんなコマンドないよぉ!!』とエラー出してくるわけ。
そういう意味で、パスを通すとは、新しいディレクトリパスを既存のPATH環境変数に追加する(コマンドの検索先を追加する)こと を指している。
コマンドには種類がある
コマンドは大きく分けて以下の3種類がある。
- エイリアス:コマンドの別名
- ビルトインコマンド:シェルの中に最初から組み込まれているコマンド
- 外部コマンド:シェルの外部にファイルとして用意されているコマンド
コマンドが上記のどれに該当するかは type
コマンドで確かめることができる。
# 使い方
$ type コマンド名
# 例えばこんな感じ
$ type grep
grep is aliased to 'grep --color=auto' # エイリアスだ!
$ type cd
cd is a shell builtin # ビルトインコマンドだ!
$ type jq
jq is /usr/bin/jq # 外部コマンドだ!
エイリアスやビルトインコマンドを使用する際には環境変数に PATH
を通す必要がない。PATH
の設定は、システム上の外部コマンドを実行する際にのみ関係することが理解できたらOK!
Linuxにおいて環境変数を設定するための方法
今回はいったん以下2つの方法を取り上げて、環境変数への PATH
を通すを説明します。
- 方法1:シェルの途中で
PATH
を通す - 方法2:初期化ファイル(
.profile
や.bash_profile
など)でPATH
を通す
方法1:シェルの途中でPATHを通す
環境変数は、シェル変数の中でも特に外部プロセスやシステム全体で使われる変数のこと。
なのでまずシェル変数について触れますが、シェル変数は文字列や数値などの値を保存する機能であり、プログラマにとっては身近なものですね。
- シェル変数の定義:
変数名=値
- シェル変数の参照:
$変数名
で、環境変数はそんなシェル変数の中の一部分にあたります。
シェル変数との具体的な違いはというと、子プロセスに変数の値が受け継がれるという点。
環境変数がサブシェルでも引き継がれることを確認する
まず、シェル変数を定義してサブシェルに引き継がれないことを確認する。
$ hoge=hogeee # シェル変数を定義
$ echo $hoge
hogeee # 出力される
$ bash # サブシェルの起動
$ echo $hoge
# 何も表示されない
上記で、プロセス上で定義したシェル変数は子プロセスには引き継がれないことが確認できたところで、環境変数を定義してみましょう。
$ fuga=fugaaa # シェル変数を定義
$ export fuga # シェル変数fugaを環境変数としても定義
$ echo fuga
fugaaa # 出力される
$ bash # サブシェルの起動
$ echo fuga
fugaaa # 出力される
環境変数に定義したら、サブシェル起動後の子プロセス上でも使用できました◎
注意すべき点は、環境変数に定義したとしても親プロセスでは使用できない点ですね。(初期化ファイルに登録しておく必要がある)
サンプルスクリプト
jq
コマンドがマシンにインストールされているとして、シェルスクリプト上で jq
コマンドを実行するために jq
コマンドのパスを通すサンプルスクリプトです。
#!/bin/bash
# PATHにjqコマンドのディレクトリパスを追加する
export PATH="$PATH:/path/to/jq"
# jqコマンドを実行する
result=$(echo '{"name": "_mi", "age": 15}' | jq '.name')
echo "Result: $result"
実行してみると、
$ bash jqsample.sh
Result: "_mi" # 出力される
こんな感じで、シェルの途中でコマンドまでのパスを環境変数として登録し、利用するという方法が1つですね。
方法2:初期化ファイルでPATHを通す
上記の登録方法はあくまで操作しているbashプロセスに対する設定であるため、別のプロセスには反映されない(※ 子プロセスには反映される)という問題がある。毎回こんな記述したくないよぉ!という場合は、初期化ファイルに登録するといい。
登録しておけば(読み込みのタイミングが違うので注意が必要だけど)シェルの起動時に設定値が自動で読み込まれるので、エイリアスや環境変数を毎回設定しなくてよくなる。
初期化のための設定ファイルである .bash_profile
や .bashrc
については、以下記事がとても分かりやすく書いてくれているので参照されたし。
コラム:外部コマンドのプログラムを確認してみる
この前インストールした jq
コマンドを見てみるよ。
$ type jq # パスを確認する
jq is /usr/bin/jq
$ cd /usr/bin/ # 移動
$ ls -l jq # ファイルの読み取り権限を確認
-rwxr-xr-x 1 root root 30872 Mar 24 2022 jq
$ cat jq # 中身の確認
// あ...文字化け...
jq
コマンドはバイナリ形式の実行ファイルだからそりゃあテキストエディタでは正しく表示することはできませんねWWW
てなわけで方向転換。
$ apt policy jq # インストール元を確認する
jq:
Installed: 1.6-2.1ubuntu3
Candidate: 1.6-2.1ubuntu3
Version table:
*** 1.6-2.1ubuntu3 500
500 http://ftp.jaist.ac.jp/pub/Linux/ubuntu jammy/main amd64 Packages
100 /var/lib/dpkg/status
どうやらインストールされている jq
パッケージのインストール元は、
http://ftp.jaist.ac.jp/pub/Linux/ubuntu になっていることが確認できたので見に行く。
が、見てもよう分からんとなり、Microsoft Copilotに聞いたらそこから見るの無理ぽい回答されたので諦めました。己の弱さを今日もまた知った。
なお、https://ftp.jaist.ac.jp/pub/Linux/ubuntu/ で提供されているのはUbuntuのパッケージリポジトリであり、特定のコマンドの実行ファイルを直接探すのではなく、パッケージマネージャ(aptコマンドなど)を通じてソフトウェアをインストールするためのものです。したがって、このURLから直接jqコマンドの実行ファイルを見つけることは難しいです。
でも探してみたら jqの公式リポジトリ があった。src
直下のファイル見ると、なんかこの辺かなーという感じで、主にC言語のプログラムファイルがありました。中身は何書いてあるか1ミリも分からない。
コマンドのプログラムファイルが見たかったらおとなしく 公式リファレンス を見ましょう!!
まとめ
パスを通す = 環境変数 PATH
に外部コマンドまでのパスを新たに追加して、ファイル名だけでコマンドを実行できるようにすること。
パスを通しておけば、シェルで打ち込んだときにコマンドを探しに行ってくれるのでコマンド(プログラム)が実行される。
おわりに
実際にコマンド打ちながら確かめる記事書くの、いいアウトプットになっていいね!