FOR BEGINNERS未経験向け

CLinux とサーバー基礎段0土台層仕組み

コマンドが動く仕組み|黒い画面で打った1行が、PATH・入出力・終了ステータスでどう実行されるか

コマンドが動く仕組み|黒い画面で打った1行が、PATH・入出力・終了ステータスでどう実行されるか

ターミナルに ls -l /var/log と打って Enter を押すと、一覧が出てきます。見よう見まねで打てるようにはなったけれど、その1行が「誰に読まれて、何を探して、どこに結果を返しているのか」は分からないまま、という人は多いはずです。だから command not found と出たときや、ファイルに保存したはずのエラーが画面に出てしまったときに、どこを見ればよいか分かりません。

この記事では、打った1行が実行されるまでを「読む → 分ける → 探す → つなぐ → 動かして返す」の順で追います。一つひとつは短い仕組みですが、つながると黒い画面のふるまいがほとんど説明できるようになります。

この記事の結論

  • ターミナルで打った1行を読んで実行しているのは、シェル(多くの Linux では bash)というプログラムです。
  • シェルは1行を空白で単語に分け、先頭の単語をコマンド名、残りを引数として扱います。
  • 名前に / が無いコマンドは、環境変数 PATH に並んだディレクトリを前から順に探して見つけます。
  • どのコマンドも、標準入力(0番)・標準出力(1番)・標準エラー(2番)の3本の口を持って動きます。
  • パイプ( )は出力を次のコマンドの入力に、リダイレクト(> や <)は出力や入力をファイルにつなぎ替えます。
  • コマンドは終わるときに終了ステータスという数字を返し、0 が成功、0 以外が失敗の合図です。

打った1行は、誰が読んでいるのか

黒い画面そのものは、文字を表示して、キーボードの入力を受け取る「窓」にすぎません。その窓の向こうで1行を読み、解釈して、プログラムを動かしているのがシェルです。Linux のサーバーでよく使われるシェルは GNU の bash で、ほかに POSIX で決められた sh(そして zsh など)があります。

たとえるなら、シェルは受付の係です。利用者(あなた)が紙に書いた依頼を読み、どの担当部署(プログラム)に回すかを決め、書類の受け取り口と返却口を用意して、担当が作業を終えたら「うまくいったか」を聞いて戻ってきます。

正確な言葉で書き直すと、シェルが1行に対してやっていることは次の順番です。

  1. 1行を読み、空白で区切って単語に分ける(クォートで囲んだ部分は1つの単語のまま)
  2. 変数($HOME など)やワイルドカード(*.log など)を実際の値に展開する
  3. > や | のようなリダイレクト・パイプの記号を見つけて、入出力のつなぎ先を決める
  4. 先頭の単語を手がかりに、実行するものを探す
  5. 見つけたプログラムを動かし、終わるのを待って、終了ステータスを受け取る

ONE LINE, FIVE STOPS1行の通り道

打った1行は、シェルに読まれてから実行される

  1. 読む

    シェルが1行を受け取る

  2. 分ける

    空白で単語に。展開も

  3. 探す

    組み込み → PATH の順

  4. つなぐ

    入力・出力・エラーの口

  5. 動かして返す

    終了ステータス 0 か否か

出典: GNU Bash Reference Manual、POSIX.1-2024 Shell Command Language

ここで大事なのは、ワイルドカードの展開やリダイレクトは、コマンドではなくシェルが先にやっていることです。ls *.log と打つと、ls が * を解釈しているのではなく、シェルが *.log を実在するファイル名の並びに置き換えてから ls に渡しています。

READ ALSO — 未経験向け・段0 土台 黒い画面の読み方|ターミナルで「いまどこにいて、何ができるか」を知る

コマンドはどこから探されるのか

先頭の単語(コマンド名)が決まると、シェルはそれを探しにいきます。GNU Bash のマニュアルでは、探す順番がおおむね次のように決まっています。

  1. 名前に / が含まれていれば、探さずにその場所のファイルをそのまま実行する(./deploy.sh や /usr/bin/ls)
  2. シェル関数として定義されていれば、それを呼ぶ
  3. シェルの組み込みコマンド(cd や echo など、シェル自身が持っている命令)なら、それを使う
  4. どれでもなければ、環境変数 PATH に並んだディレクトリを前から順に探し、同じ名前の実行できるファイルを見つける

PATH の中身は echo $PATH で見られます。/usr/local/bin:/usr/bin:/bin のように、コロンで区切ったディレクトリの一覧です。前から順に見るので、同じ名前のプログラムが2か所にあれば、前にあるほうが使われます。

cd が組み込みなのには理由があります。いまいるディレクトリは、シェル自身が覚えている状態です。別のプログラムとして動かすと、そのプログラムの中で移動しても、終わったときにシェルの居場所は変わりません。だからシェル自身がやる必要があります。

シェルが何をどう見つけるかは type で確かめられます。

$ type cd
cd is a shell builtin
$ type ls
ls is /usr/bin/ls

名前に / が無ければ、いまいるディレクトリは通常探されません。自分で作ったスクリプトを deploy.sh とだけ打っても動かず、./deploy.sh と打つと動くのはこのためです。

見つからなければ command not found と表示され、終了ステータスは 127 になります。見つかったのに実行できない(実行の権限が無いなど)ときは 126 です。権限の読み方は次に読む「ファイルと権限の仕組み」で扱います。

CHECK 1ここまでの確認

ls と打ったとき、シェルがそのプログラムを探しにいく場所を決めているのはどれ?

答えと解説

答え: 2. 環境変数 PATH に並んだディレクトリを、左から順に

名前に / が無いコマンドは、組み込みコマンドでなければ PATH に並んだディレクトリを前から順に探します。「コマンドはどこから探されるのか」の章を読み直してください。

引数とオプションとは

コマンド名の後ろに並ぶ単語は、すべて引数としてプログラムに渡されます。ls -l /var/log なら、プログラム ls に -l と /var/log の2つが渡ります。

その引数のうち、- で始まってふるまいを変えるものを慣習としてオプションと呼びます。-l は「詳しい形式で出す」というオプション、/var/log は「どこを見るか」という対象です。

書き方 例 何を伝えるか
短いオプション ls -l 1文字で、ふるまいを変える
短いオプションをまとめる ls -la -l と -a を一度に(多くのコマンドで使える)
長いオプション ls --all 単語で、ふるまいを変える(GNU のコマンドに多い)
値を取るオプション head -n 5 app.log オプションのあとに値を置く
対象(オペランド) ls /var/log 何に対して動くか

どのオプションがあるかはコマンドごとに違います。確かめる場所は man ls のようなマニュアル(man ページ)か、多くのコマンドで使える --help です。オプションの意味を覚えるより、この2つを引く癖をつけるほうが、知らないコマンドに出会ったときに強くなれます。

空白の入ったファイル名には注意が要ります。シェルは空白で単語を分けるので、rm my file.txt は my と file.txt の2つを消そうとします。rm "my file.txt" のようにクォートで囲むと、1つの引数として渡ります。

標準入力・標準出力・標準エラーとは

どのプログラムも、動き始めた時点で3本の口を持っています。man7.org の stdin(3) にあるとおり、入力の口が標準入力、出力の口が標準出力、エラーや診断を出す口が標準エラーで、それぞれ 0番・1番・2番の番号(ファイルディスクリプタ)が付いています。

THREE STREAMS3本の口

どのコマンドも、入口1本と出口2本を持って動く

  1. 0番

    標準入力 — 読むものが入ってくる口

    何もしなければキーボード。< でファイルから

  2. 1番

    標準出力 — 結果を出す口

    何もしなければ画面。> でファイルへ、| で次のコマンドへ

  3. 2番

    標準エラー — エラーや診断を出す口

    何もしなければ画面。2> で別のファイルへ

出典: stdin(3) Linux manual page(man7.org)

何も指定しなければ、0番はキーボード、1番と2番はどちらも画面につながっています。だから結果もエラーも同じ画面に混ざって見えます。出口が2本に分かれていることが、次のリダイレクトの前提になります。

リダイレクト — つなぎ先をファイルに替える

リダイレクトは、口のつなぎ先をファイルに付け替える記号です。付け替えをするのはシェルで、コマンドの側は「いつもの口に書いているだけ」です。

記号 例 何が起きるか
> ls > list.txt 標準出力をファイルへ(中身を上書き)
>> date >> run.log 標準出力をファイルの末尾に追記
< sort < names.txt 標準入力をファイルから読む
2> ls /nope 2> err.txt 標準エラーだけをファイルへ
> file 2>&1 make > build.log 2>&1 標準出力をファイルへ向け、標準エラーも同じ先へ

2>&1 は「2番を、いま1番が向いている先と同じにする」という意味です。Bash のマニュアルが注意しているとおり、リダイレクトは左から順に処理されるので、書く順番で結果が変わります。> build.log 2>&1 なら両方がファイルに入りますが、2>&1 > build.log と逆に書くと、2番は「その時点の1番」=画面を向いたまま残ります。

リダイレクトで起きやすい事故

  • > は中身を消してから書くので、残したいログに > を使うと過去の記録が消える(追記は >>)
  • sort data.txt > data.txt のように、読むファイルと書くファイルを同じにすると、読む前に空になる
  • > file だけでエラーも保存したつもりになり、肝心のエラーが画面に流れて消えている

パイプ — 出力を次のコマンドの入力にする

パイプ(|)は、左のコマンドの標準出力を、右のコマンドの標準入力に直結します。ファイルを間に置かずに、小さなコマンドを組み合わせて1つの作業にできるのが Linux の道具の強みです。

$ cat access.log | grep " 500 " | wc -l

アクセスログを読み、「500」を含む行だけを残し、その行数を数える、の3段です。パイプが渡すのは標準出力だけなので、エラーも次に渡したいときは 2>&1 |(bash なら |&)で合流させます。

PIPE OR REDIRECT出力の行き先

加工を続けるならパイプ、残すならリダイレクト

パイプ |

コマンド → コマンド

  • 標準出力を次の標準入力へ直結
  • ファイルを間に置かない
  • 例: cat log | grep 500 | wc -l

リダイレクト > >> <

コマンド ⇄ ファイル

  • 出力をファイルへ(> は上書き)
  • >> は末尾に追記
  • 例: make > build.log 2>&1
出典: GNU Bash Reference Manual(Pipelines / Redirections)

CHECK 2ここまでの確認

ls /nope > out.txt と打っても、エラーの文が画面に出てしまうのはなぜ?

答えと解説

答え: 1. 「>」が標準出力(1番)だけをファイルに向け、エラーは標準エラー(2番)から出ているから

出口は標準出力と標準エラーの2本に分かれています。「>」は1番だけを付け替えるので、2番のエラーは画面に残ります。「標準入力・標準出力・標準エラーとは」の章を読み直してください。

終了ステータスとは|成功と失敗を数字で返す

コマンドは終わるときに、画面に出す文字とは別に、シェルへ小さな数字を1つ返します。これが終了ステータスです。POSIX と Bash のマニュアルが定めるとおり、0 が成功、0 以外が失敗です。

直前のコマンドの終了ステータスは $? で見られます。

$ ls /var/log > /dev/null
$ echo $?
0
$ ls /nope
ls: cannot access '/nope': No such file or directory
$ echo $?
2

0 以外の値の中身はコマンドごとに決まっていて、man ページの EXIT STATUS の欄に書いてあります。シェルが決めている値もあります。

値 意味 出どころ
0 成功 各コマンド
1〜125 のどれか そのコマンドが決めた失敗の種類 各コマンド(man ページの EXIT STATUS)
126 見つかったが実行できない シェル(POSIX)
127 コマンドが見つからない シェル(POSIX)
128 より大きい値 シグナルで止められた(bash では 128+シグナルの番号) シェル(bash)

人は画面の文字で成否を読みますが、スクリプトや自動化の仕組みはこの数字で成否を読みます。make && ./deploy.sh の && は「左が 0 で終わったときだけ右を動かす」という意味で、ビルドに失敗したらデプロイに進まない、という安全装置になります。

パイプでつないだときは、全体の終了ステータスは既定でいちばん右のコマンドの値です。途中のコマンドが失敗しても、最後が成功すれば 0 に見えます。途中の失敗も拾いたいスクリプトでは、bash の set -o pipefail を使います。

この「0 なら次へ、0 以外なら止まる」という約束は、後の段で学ぶ CI/CD(コードの変更を自動でテストして届ける仕組み)でも、そのまま土台として使われています。

CHECK 3ここまでの確認

直前のコマンドが成功したかどうかを、シェルに尋ねる書き方はどれ?

答えと解説

答え: 2. echo $?

直前のコマンドの終了ステータスは $? に入っています。0 なら成功、0 以外なら失敗です。「終了ステータスとは」の章を読み直してください。

参照ソース

SCOREこの記事の確認テスト

0/3問 正解

各章の「ここまでの確認」に答えると、ここに集まります。結果はこの端末のブラウザにだけ残り、サーバには送りません。

FAQよくある質問

command not found と出たら、まず何を確かめればよいですか?

綴りの誤りがないか、そのプログラムがインストールされているか、置かれたディレクトリが PATH に入っているかの3つです。echo $PATH で探す場所の一覧を、type コマンド名 でシェルがそれをどう見つけているかを確かめられます。見つからなかったときの終了ステータスは 127 です。

./ を付けないと、いまいる場所のスクリプトが動かないのはなぜですか?

名前に / が無いと、シェルは PATH に並んだディレクトリだけを探し、いまいるディレクトリは通常そこに入っていないからです。./script.sh のように / を含めると、PATH を探さずにその場所のファイルを直接動かします。見つかっても実行の権限が無ければ、終了ステータス 126 で止まります。

パイプとリダイレクトはどう使い分ければよいですか?
出力を次のコマンドに渡して加工を続けたいならパイプ( )、ファイルに残したい・ファイルから読ませたいならリダイレクト(> や <)です。パイプは標準出力だけを次に渡すので、エラーも渡したいときは 2>&1 で標準エラーを標準出力に合流させます。

SAME TRACK同じ領域の記事

黒い画面の読み方|ターミナルで「いまどこにいて、何ができるか」を知る
黒い画面の読み方|ターミナルで「いまどこにいて、何ができるか」を知る 段0 土台とは
Linux とは何か|Windows との違いと、サーバーがほぼ Linux で動いている理由
Linux とは何か|Windows との違いと、サーバーがほぼ Linux で動いている理由 段0 土台とは
プロセスとサービスとは何か|systemd がサーバーのプログラムを動かし続ける仕組み
プロセスとサービスとは何か|systemd がサーバーのプログラムを動かし続ける仕組み 段1 入口とは