← ミライズ労務コンサルティング開発知識シリーズ 目次(全6部)
hokama@mirise ~ $

シェル入門

ターミナルの黒い画面に文字を打ち込んで Enter を押すと、そこで何が起きているのか。
カーネル、シェル、プロセスという言葉を、順番に、一つずつ。

$ whatis interface

GUI と CLI ─ インターフェースとは

「接触面」という言葉から入るのが近道です。

interface は inter(間)+ face(面)で、接触面を意味します。異なる二つのものが触れ合う境目のことです。

ユーザーインターフェース(UI)とは、人間と機械が触れ合う面のこと。その面が何でできているかで名前が変わります。

略語正式名称面の材質
GUIGraphical User Interface図形(アイコン・ボタン・窓)
CLICommand Line Interface文字(打ち込むコマンド)

読み方は「グイ」または「ジーユーアイ」、「シーエルアイ」です。

GUI ─ 見えているものを指す

アイコンをクリックする、ウィンドウをドラッグする、ボタンを押す。マウスやタッチで、画面に見えているものを直接指し示す方式です。Finder、エクスプローラー、スマートフォンのホーム画面 ─ 日常的に触れているものはほぼすべて GUI です。

CLI ─ 名前を呼んで指示する

文字を1行打ち込んで Enter を押す。この資料で扱っている黒い画面がこれです。

重要なのは、GUI も CLI も、やっていることは同じだという点です。どちらも OS への窓口であって、最終的にカーネルへ同じ依頼を出しています。

GUI アイコンをドラッグする CLI mv a.txt docs/ と打つ 同じシステムコール カーネルへの依頼 カーネル 実際にファイルが動く
違うのは依頼の伝え方だけで、その先で起きることは完全に同一です。

なぜ両方が生き残っているのか

人間の記憶には二種類あります。再認(見せられれば「あ、これだ」と分かる)と、想起(何も見ずに自分で思い出す)です。

GUI は再認に頼る設計です。メニューを開けば選択肢が並んでいるので、覚えていなくても探せます。CLI は想起に頼る設計です。mv という名前を知らなければ、そのコマンドは存在しないのと同じです。

この違いが、そのまま得手不得手になります。

GUICLI
初めての人易しい難しい
慣れた人遅い速い
知らない機能を見つける得意苦手
同じ作業を100回苦手得意
手順を人に渡す画面録画や手順書が必要文字列をそのまま送れる
遠隔のサーバー操作重い軽い
差が決定的になる場面

ファイル100個の名前を一括で変えるとします。GUI なら100回クリックして100回タイプする。30分かかり、来月また同じことをします。CLI なら1行です。1秒で終わり、しかもその1行を保存しておけば来月もそのまま使えます

CLI が速いのはタイプが速いからではありません。手順そのものを文字として保存・再利用・送信できるからです。マウスの動きは保存できません。

TUI と API ─ 近い親戚

文字だけで GUI のような画面を描く方式を TUI(Text User Interface)と呼びます。文字で枠線を引き、カーソルキーで項目を選ぶ。Claude Code がまさにこれで、CLI と GUI の中間にあたります。

もう一つ、API(Application Programming Interface)があります。接触面の両側がプログラムとプログラムである場合の呼び名です。

種類接触面の両側
GUI・CLI・TUI人間 ↔ 機械
API機械 ↔ 機械

業務システムの画面を人が操作するのが GUI、そこへプログラムがアクセスするのが API という関係になります。

どちらか一方を選ぶものではありません。VS Code は GUI の中にターミナルが埋め込まれていますし、GitHub も Vercel も同じ操作を Web 画面とコマンドの両方で提供しています。探すとき・眺めるときは GUI、繰り返すとき・人に渡すときは CLI。これが実務的な落としどころです。

$ whatis shell

シェルとは何か

名前がそのまま答えになっています。

shell は英語で「殻」です。何の殻かというと、OS の中心部を包む殻です。

その中心部は カーネル(kernel)と呼ばれます。kernel は「核」「木の実の中身」という意味の単語です。つまり OS を木の実に見立てて、食べる部分=カーネル、それを包む殻=シェル、と名付けられています。比喩というより、そのままの命名です。

なぜ殻が必要なのか

カーネルは、メモリの割り当て、ディスクへの読み書き、CPU の配分といった実務を担当しています。ただしカーネルは日本語も英語も理解しません。決められた形式でプログラムから呼び出されること(システムコール)しか受け付けません。

人間はその形式で入力できません。だから間に「人間の言葉を受け取って、カーネルへの依頼に翻訳する」層が必要になる。それがシェルです。

実は GUI もシェルです

ここが一番の勘所かもしれません。Windows のエクスプローラーや Mac の Finder も、役割としてはシェルです。実際 Microsoft は explorer.exe を公式に「Windows シェル」と呼んでいます。

フォルダのアイコンをダブルクリックする。cd project と打つ。どちらも「そのフォルダを開け」とカーネルに依頼しているという点で同じです。窓口の形が「マウスで触る」か「文字を打つ」かの違いにすぎません。

種類具体例
GUI シェルエクスプローラー、Finder
CLI シェルbash、zsh、PowerShell、cmd.exe

ですので「シェル=黒い画面」ではありません。シェル=OS への窓口であって、黒い画面はその一形態です。

シェルがしている仕事は4つだけ

待つ(プロンプトを出して入力を待つ)。解釈する(打たれた文字列を切り分ける)。依頼する(該当のプログラムを探して動かすようカーネルに頼む)。表示する。そしてまた最初に戻る。延々とこの繰り返しです。

意外な事実

ls は bash の機能ではありません。/bin/ls という独立したプログラムファイルです。bash はそれを探して起動しているだけで、一覧表示の処理自体は何もしていません。

だから「探しに行く場所のリスト」が必要になります。それが PATH です。ここがつながると、command not found というエラーが「そんなコマンドは存在しない」ではなく「リストに載っている場所を全部見たけれど、その名前のファイルが無かった」という意味だと分かります。

$ whatis terminal

ターミナルとシェルは別物

多くの人がここで混乱します。

ターミナル(端末エミュレータ)は、文字を表示しキー入力を受け取る「窓」です。Windows Terminal、Mac の Terminal.app がこれにあたります。

シェルは、打ち込まれた文字列を解釈して実際に実行する「頭脳」です。bash、zsh、PowerShell、cmd.exe がこれにあたります。

窓を替えても中身のシェルは同じ、逆に同じ窓の中でシェルだけ切り替える、ということが起こります。

あなた(キーボード入力) ターミナル(端末アプリ) 文字を映すだけの窓 シェル(コマンド解釈器) bash / zsh / PowerShell / cmd.exe OS カーネル ファイル・プロセス・通信を管理 ハードウェア
入力は上から下へ、結果は下から上へ戻ってきます。シェルはこの真ん中で翻訳をしている層です。
$ man bash

bash とは

「シェル」が分類名で、bash はその中の具体的な1つ。ブラウザと Chrome の関係です。

bash は Bourne Again SHell の略で、読みは「バッシュ」です。

1979年に Stephen Bourne という人が作った sh(Bourne シェル)が Unix の標準でした。1989年にそれを一から作り直したのが bash です。"Bourne Again" は "born again"(生まれ変わった)との駄洒落になっています。つまり bash は、40年以上使われてきたシェルの現在の主流版です。

Unix / Linux 系 sh(Bourne シェル) 1979年・すべての元祖 bash 1989年・Linux 標準 zsh 1990年・Mac 標準 ksh 1983年・業務系で残存 Windows 系(別の家系・血縁なし) cmd.exe 1993年・MS-DOS 由来 PowerShell 2006年・.NET 基盤
bash と zsh は兄弟なので操作感はほぼ同じです。PowerShell はまったく別の家系で、文法も設計思想も異なります。

どこで bash に出会うか

Linux サーバーはほぼ必ず bash です。Mac は 2019年以降 zsh が標準ですが、基本操作は bash と同じです。Windows には標準では入っておらず、Git をインストールすると付いてくる Git Bash か、WSL を導入すると使えるようになります。

画面の読み方

ここが最初につまずくところです。ネットの記事にこう書いてあったとします。

$ npm install

この先頭の $打つ文字ではありません。「入力を待っています」という合図の記号です。実際に打つのは npm install の部分だけ。$ npm install をまるごとコピーして貼り付けるとエラーになります。

実際の画面はこう見えています。

hokama@MacBook ~/project $ ls
部分意味
hokamaログインしているユーザー名
MacBookこの機械の名前
~/project今いるフォルダ(~ はホーム)
$プロンプト記号。ここから右が自分の入力
ls実際に打ったコマンド

この $ より左の部分をまとめてプロンプトと呼びます。設定で見た目が変わるので、人によって表示は違います。

まず覚える5つ

$ pwd # 今どこにいるか表示 $ ls # ここに何があるか一覧 $ cd project # project フォルダへ移動 $ cd .. # 一つ上へ戻る $ cat memo.txt # ファイルの中身を表示

この5つだけで「フォルダの中を歩き回って中身を確認する」ができます。GUI でいうエクスプローラーの操作に相当します。

$ strace ls

Enter を押してから、結果が出るまで

ここまでの話が全部つながる箇所です。ls -l *.txt と打った場合を追います。

  1. ターミナル

    キーを打つ

    キーを1つ押すたびに、その文字がシェルへ送られ、同時に画面へ返って表示されます。これをエコーと呼びます。バックスペースや矢印キーで打ち間違いを直せるのも、この段階でシェルが編集機能を提供しているからです。

  2. ターミナル

    Enter を押す

    改行文字が送られます。シェルはこれを見て「1行の入力が確定した」と判断し、受付を終えて処理に入ります。

  3. bash

    切り分ける

    受け取った文字列を、意味のある単位に分解します。ls がコマンド名、-l*.txt が引数、という具合です。

  4. bash

    展開する

    *.txt を、実際に存在するファイル名の並びに置き換えます。~ はホームのパスに、$HOME のような変数はその値に置き換わります。

    重要

    ワイルドカード * を展開しているのは ls ではなくシェルです。ls は展開が済んだ後のファイル名リストを受け取るだけで、* という記号を見ることすらありません。

  5. bash

    探す

    まず自分が内蔵している機能(ビルトイン)かどうかを確認します。違えばエイリアス(別名)かを確認。それも違えば、PATH に並んだフォルダを先頭から順に見て ls という名前のファイルを探します。/usr/bin/ls を発見。見つからなければ、ここで command not found が出て終わります。

  6. bash

    分裂して、変身する

    ここが最も独特な部分です。bash はまず自分自身のコピーを作ります(fork)。次に、そのコピーの中身を /usr/bin/ls のプログラムで丸ごと上書きします(exec)。この瞬間、bash の複製だったものが ls になります。

    親である bash は、その横で子の終了をただ待ちます。

  7. カーネル

    カーネルに頼む

    ls がカーネルに「このフォルダの中身を教えてほしい」と依頼します(システムコール)。カーネルがディスクを読み、結果を返します。ls はそれを整形して標準出力へ書き出します。

  8. ターミナル

    表示して、また待つ

    標準出力の先はターミナルにつながっています。ターミナルは届いた文字をフォントで描画し、ここで初めて人間の目に見えます。

    ls終了コード(成功なら 0)を残して消えます。bash は待機から復帰してその値を $? に保存し、新しいプロンプトを表示して 1 に戻ります。

① 最初 ② fork ─ 分裂 ③ exec ─ 変身 bash bash(親) bash の複製 bash(待機) ls 入力を待っている 自分のコピーを作る コピーの中身が ls になる
コマンドを実行するたび、この分裂と変身が起きています。bash 自身は決して ls になりません。

この流れから分かる、cd だけが特別な理由

ここまで読むと、ある疑問が解けます。なぜ cd は独立したプログラムではなく、bash が内蔵しているのか。

もし cd/bin/cd という独立したプログラムだったとします。すると上の図のとおり、bash は分身を作り、その分身が cd になってフォルダを移動します。しかし移動したのは分身であって、親の bash ではありません。分身が仕事を終えて消えた瞬間、何事もなかったように元の場所へ戻ってしまいます。

だから cd は bash 自身が実行しなければならない。「自分の居場所を変えられるのは自分だけ」という、ごく当たり前の制約から来ています。

なぜ文字で操作するのか

1970年代は画面に文字しか出せなかったので必然でした。今も残っているのは、文字にしかない性質があるからです。記録できる(打った内容を保存すればそのまま再実行できる)。渡せる(手順を文字列で人に送れる。マウスの動きは送れない)。連結できる| でコマンド同士をつなげられる)。

マウス操作は速いけれど、100回繰り返すことも、人に正確に渡すこともできません。Claude Code が文字でやりとりしているのも同じ理由です。

$ man 7 philosophy

設計の原則 ─ なぜこの作りなのか

ここまでの挙動は、すべて数個の原則から導かれています。

Unix は1969年、ベル研究所の片隅で始まりました。Ken Thompson が、使われなくなった小型機を借りて、会社の正式なプロジェクトでもなく作り始めたものです。メモリは数キロバイト。大きなプログラムは物理的に入りませんでした。

この制約が、そのまま設計思想になりました。そして半世紀後、制約がとうに消えた今も、思想のほうが残っています。残ったのは、それが単なる妥協ではなく正しかったからです。

原則 1すべてはファイルである

ファイル、フォルダ、キーボード、画面、プリンタ、ネットワーク接続。Unix ではこれらがすべて「開く・読む・書く・閉じる」という同じ4つの操作で扱えます。

だから > でファイルへ送るのも画面へ送るのも同じ構文になります。/dev/null という「書き込むと消える偽ファイル」が捨て場として機能します。標準入力・標準出力・標準エラー出力は、実体としては 0番・1番・2番というただのファイル番号です。

効果は、覚えることが激減することです。新しい種類の装置が登場しても、既存の知識でそのまま扱えます。

原則 2一つのことを、うまくやる

ls は一覧するだけです。並べ替えも検索も集計もしません。sort は並べ替えるだけ、grep は探すだけ。

だから ls* を展開しません。それはシェルの仕事だからです。役割が重ならないよう、徹底して分けられています。

効果は、各プログラムが小さく保たれ、正しさを確認しやすくなることです。一つ直しても他が壊れません。

原則 3組み合わせて使う

原則1と2が揃うと、掛け算になります。100個の小さな道具は、100通りではなく組み合わせの数だけの使い道を持ちます。| はその接続端子です。

大きな一枚岩を作るのではなく、小さな部品を作って、必要なときに毎回組み立てる。これが Unix の中心的な発想です。

一枚岩の設計 Unix の設計 1つの巨大なプログラム 機能追加のたび全体が複雑に ls ── 一覧する | grep ── 絞り込む | sort ── 並べ替える | head ── 先頭だけ出す 全体を理解しないと直せない 小さく作り、必要な形につなぐ
右側の4つは、順番を変えれば別の道具になります。部品が増えるほど、組み合わせは加速度的に増えます。

原則 4テキストを共通語にする

すべてのプログラムが同じ言語 ─ つまり文字 ─ を話せば、どれとどれでも接続できます。もし各プログラムが独自形式を使えば、N個のプログラムをつなぐのに N² 個の変換器が必要になります。

副次的な効果として、人間が中身を読めます。だから Unix では設定ファイルもログもテキストです。JSON、YAML、Markdown、そして Claude Code に渡す指示書も、すべてこの思想の延長線上にあります。

原則 5沈黙は成功のしるし

成功したコマンドは何も言いません。cp a.txt b.txt が成功すると、画面は無反応のままです。

初学者が最も戸惑う挙動ですが、理由があります。出力はパイプで次のコマンドへ流すためのものだからです。もし「コピーしました」と表示したら、その一文が次のコマンドへ流れ込んで邪魔になります。

だから、うまくいったかどうかは $? という終了コードで確認します。何も出ないのは、うまくいった証拠です。

原則 6透明であること

設定は読めるテキストファイルに置く。動作は追跡できるようにする。隠された状態を作らない。

これは「直せる」という価値に直結します。GUI の「なぜか動かない」に対して、CLI は「どこで止まったか」が見えます。エラーメッセージが読めるのも、途中経過を確認できるのも、この原則の帰結です。

原則 7人間の時間のほうが高い

機械の1秒を節約するより、人間の1時間を節約するほうが価値がある。この判断基準が、多少非効率でも読みやすく書きやすい形を選ばせています。

自動化の本当の正当化もここにあります。速いからではなく、人間が同じことを二度やらなくて済むからです。

原則の限界 ─ PowerShell はなぜ背いたか

原則4(テキストが共通語)には代償があります。テキストは構造を失うということです。

ls -l の出力からファイルサイズを取り出すには「左から5列目」と指定するしかありません。表示形式が少し変わっただけで壊れます。日付の書式が変わっただけで動かなくなります。

PowerShell はこの弱点を見て、テキストではなくオブジェクトを流す設計を選びました。得たものは頑健さ、失ったものは他言語のプログラムとの接続性です。どちらが正しいかではなく、何を優先するかの選択だと理解するのが正確です。

自分で道具を作るときの指針

これらの原則は、スクリプトや業務ツールを自作するときにそのまま使えます。

原則実務への翻訳
一つのことを1つのスクリプトに1つの役割。「集計もメール送信もする」は分ける
組み合わせる単体で完結させず、出力を次へ渡せる形にする
テキストを共通語に出力は CSV か JSON。人が読めて、機械も読める形式を選ぶ
沈黙は成功正常時は静かに。異常時だけ、何がどこで起きたかを出す
透明であること設定値をコード内に埋め込まず、外部のテキストファイルに置く
人間の時間が高い1回しかやらない作業は自動化しない。3回目からが分岐点
現在地

Claude Code はこの系譜の直系にあります。小さな道具(読む・書く・探す・実行する)を組み合わせて動く。指示はテキストで渡す。状態は隠さずファイルに置く。

1969年に数キロバイトのメモリの中で生まれた作法が、そのまま現在の道具の作法になっているということです。

PS> Get-Member

PowerShell という別の家系

bash と PowerShell の決定的な違いは、コマンドの間を流れるものの正体です。

bash や cmd はコマンド間でテキストを受け渡します。PowerShell は .NET のオブジェクトを受け渡します。

bash / cmd ─ テキストが流れる コマンド実行 ls -l / tasklist テキストの塊 ただの文字列 文字列を検索 列がずれると破綻 PowerShell ─ オブジェクトが流れる コマンドレット実行 Get-Process オブジェクト .Name .Id .CPU プロパティで絞る 列の位置に依存しない
テキスト方式では出力の見た目が変わると処理が壊れます。オブジェクト方式では構造を直接扱うので壊れません。

たとえば CPU を多く使っているプロセスを上位5件出す場合。

PS> Get-Process | Where-Object { $_.CPU -gt 100 } | Sort-Object CPU -Descending | Select-Object -First 5

$_ は「パイプで流れてきた今の1件」を指します。文字列を切り出しているのではなく、CPU というプロパティを直接見ているのがポイントです。

コマンドレットの名前は必ず動詞-名詞の形をとります(Get- Set- New- Remove- Start- Stop- Test- Export-)。この規則を知っているだけで、知らないコマンドの意味が推測できます。

自習の起点になる3つ

PowerShell はこの3つを覚えれば独力で進められます。

PS> Get-Command *service* # コマンドを探す PS> Get-Help Get-Process -Full # 使い方を読む PS> Get-Process | Get-Member # 何が入っているか調べる

とくに Get-Member が要です。パイプの左から何が流れてきているのか、どんなプロパティを持っているのかが見えるので、「オブジェクトが流れる」という感覚が身体に入ります。bash なら man コマンド名、cmd なら コマンド /? が同じ役割です。

$ help

基礎用語

これだけ押さえておけば、たいていの技術記事は読めます。

カーネル / kernel
OS の中心部。メモリ、ディスク、CPU を実際に管理している。人間とは直接やりとりしない。
シェル / shell
カーネルを包む殻。人間の入力を受け取り、カーネルへの依頼に翻訳する。
ターミナル / terminal
文字を表示し入力を受け取る窓。シェルそのものではない。
プロンプト / prompt
入力を待っている合図。$%> で終わる行。この記号自体は打たない。
パス / path
ファイルの住所。/Users/hokama/project のような絶対パスと、.(今いる場所)..(一つ上)~(ホーム)を使う相対パスがある。
カレントディレクトリ
今いるフォルダ。コマンドはここを基準に動く。pwd で確認できる。
環境変数 / PATH
OS が持つ設定値。とくに PATH は「実行ファイルを探しに行く場所のリスト」で、コマンドが見つからない原因の大半はこれ。
標準出力 / 標準エラー出力
正常な結果とエラーメッセージは別の管を通る。だから > でファイルに落としてもエラーだけ画面に残る。
パイプ |
前のコマンドの出力を、次のコマンドの入力にそのまま渡す。
リダイレクト > >>
出力を画面ではなくファイルへ送る。> は上書き、>> は追記。
引数 / オプション
ls -l /tmp-l がオプション、/tmp が引数。オプションは動作を変える指定。
プロセス / PID
動作中のプログラム1つ1つ。それぞれに PID という番号が付く。
終了コード / exit code
成功なら 0、失敗なら 0 以外。bash では $?、PowerShell では $LASTEXITCODE、cmd では %ERRORLEVEL% で確認。
ビルトイン / builtin
シェル自身が内蔵している機能。cd echo export など。独立したプログラムではない。
エイリアス / alias
コマンドの別名。PowerShell では lsGet-ChildItem の別名として登録されている。
管理者権限
システム領域を触る操作に必要。Windows は「管理者として実行」、Mac / Linux は sudo
$ alias

コマンド対応表

同じ操作が、環境によってどう呼ばれるか。

やりたいことcmdPowerShellbash / zsh
一覧表示dirGet-ChildItemls
移動cdSet-Locationcd
今いる場所cdGet-Locationpwd
中身を見るtypeGet-Contentcat
コピーcopyCopy-Itemcp
移動 / 改名moveMove-Itemmv
削除delRemove-Itemrm
フォルダ作成mkdirNew-Itemmkdir
検索findstrSelect-Stringgrep
画面クリアclsClear-Hostclear
環境変数%PATH%$env:PATH$PATH
使い方を見るcmd /?Get-Helpman

PowerShell では ls cd cat といった Unix 風の別名がそのまま使えるので、Mac と Windows の行き来はさほど負担になりません。

$ echo $?

つまずきやすいところ

実際に手を動かすと必ず出会う5つ。

スクリプトが実行できない(PowerShell)

.ps1 を実行しようとするとセキュリティエラーが出ます。既定でスクリプト実行が禁止されているためです。一度だけ以下を実行します。

PS> Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

インストールしたはずのコマンドが見つからない

PATH の変更は、すでに開いているターミナルには反映されません。ターミナルを閉じて開き直すだけで解決することがほとんどです。

日本語が文字化けする

PowerShell 5.1 は日本語環境で Shift-JIS 系を既定にします。UTF-8 のファイルが化けたら PowerShell 7 へ移るのが早道です。cmd の場合は chcp 65001 で UTF-8 に切り替わります。

ポートを掴んだまま離さない Node

# PowerShell PS> Get-Process node | Stop-Process -Force PS> Get-NetTCPConnection -LocalPort 3000 | Select-Object OwningProcess # Mac / Linux $ lsof -i :3000 $ kill -9 <PID>

&& が効かない

PowerShell 5.1 では使えません。7 以降で使えるようになりました。5.1 なら ; で区切ります。bash では最初から使えます。

最後に

ここまでの話は、結局のところ一つのことに集約されます。コマンドを打つとは、シェルに翻訳を頼むことであり、シェル自身はほとんど何もしていない、ということです。

エラーが出たときは「シェルの段階で止まったのか、その先のプログラムで止まったのか」を切り分けると、原因にたどり着くのが速くなります。