CLIとシェル入門

目次

概要

CLI(Command Line Interface)は、文字でコマンドを入力してコンピュータを操作するインターフェースです。GUIの代替ではなく、開発、運用、検証、自動化、ログ解析を支える共通言語です。

要点

CLIの本質は「操作をテキストとして表し、再実行し、組み合わせ、自動化できる」ことです。標準入出力、パイプ、リダイレクト、exit code、環境変数、引用符、展開を理解すると、OS、Git、CI/CD、クラウド操作、データ処理が同じ見方で扱えます。

CLIを学ぶ意味

CLIは、慣れるまで少し不親切に見えます。ボタンやメニューが見えないため、何を入力すればよいか分かりにくいからです。しかし、いったん基本をつかむと、GUIでは難しいことが簡単になります。

CLIが強い場面です。

  • 同じ操作を何度も繰り返す
  • 作業手順をREADMEやCIに残す
  • 大量のファイルをまとめて処理する
  • ログから必要な行だけを取り出す
  • サーバーやコンテナの中で状態を確認する
  • エラーの再現手順を共有する
  • 操作をスクリプトにして自動化する

たとえば、GUIで「あるディレクトリ以下のMarkdownの行数を全部数える」には専用機能が必要ですが、CLIなら小さな道具をつなげられます。

find . -name "*.md" -print0 | xargs -0 wc -l

この1行は、ファイル検索、区切り文字の扱い、引数化、行数カウントを組み合わせています。CLIを学ぶとは、個別コマンドを暗記することではなく、この「組み合わせ方」を学ぶことです。

シェルとは何か

シェルは、人間が入力したコマンドを解釈し、プログラムを起動するソフトウェアです。単なるコマンド入力欄ではなく、変数、制御構文、関数、引用、履歴、job controlなどを備えたプログラミング環境でもあります。

代表的なシェルです。

シェル 特徴
sh POSIX系の基本シェル。移植性を重視するスクリプトで使われる
bash Linuxで広く使われる。スクリプト用途の情報が多い
zsh macOS標準。補完や対話機能が強い
fish 対話的な使いやすさを重視。POSIX shとは互換でない部分が多い

この章では、macOSやLinuxでよく使う shbashzsh を前提にします。移植性が必要なスクリプトではPOSIX sh に寄せ、開発プロジェクト内のスクリプトでは bash を明示する、という使い分けが現実的です。

#!/usr/bin/env sh
#!/usr/bin/env bash

先頭行のshebangは、そのファイルをどのインタプリタで実行するかを指定します。bash 固有機能を使うなら、sh ではなく bash を明示します。

コマンドが実行されるまで

シェルは、入力された文字列をそのままOSに渡しているわけではありません。Bashの説明では、シェルは入力を読み、単語と演算子に分け、構文として解釈し、展開し、リダイレクトを処理し、コマンドを実行し、終了状態を取得します。

流れを単純化すると次のようになります。

  1. 入力を読む
  2. 単語、演算子、引用符を解釈する
  3. 変数展開、コマンド置換、ワイルドカード展開を行う
  4. リダイレクトを準備する
  5. 組み込みコマンド、関数、外部コマンドを探す
  6. コマンドを実行する
  7. exit codeを返す

この順序を知ると、多くの事故を説明できます。

name="my file.txt"
cat $name

これは myfile.txt という2つの引数として扱われる可能性があります。意図したファイル名を1つの引数として渡すには、引用します。

cat "$name"

シェルは便利ですが、便利さの多くは「実行前に文字列を変形する」ことから来ています。したがって、CLIの安全性は、展開のタイミングを理解しているかに大きく左右されます。

CLIの全体像

CLIでは、人間、シェル、OSプロセス、ファイルが次のようにつながります。

flowchart LR U["人間<br>コマンドを入力"] --> S["シェル<br>解析・展開・実行"] S --> P["プロセス<br>コマンド本体"] P --> FD["file descriptor<br>0 stdin / 1 stdout / 2 stderr"] FD --> F["ファイル / 端末 / pipe"] F --> N["次のコマンド<br>または保存先"]

CLIの学習では、まずこの流れを押さえます。コマンドの名前だけを覚えるより、「入力はどこから来るか」「出力はどこへ行くか」「失敗はどう伝わるか」を見る方が応用しやすくなります。

観点 確認すること 代表コマンド
現在地 どのディレクトリで作業しているか pwd, ls
入力 ファイルか、標準入力か、引数か <, pipe, xargs
出力 端末か、ファイルか、次のコマンドか >, >>, pipe
失敗 exit codeとstderrを見る $?, 2>
再現性 手順をscript化できるか sh, bash, Makefile

CLIは、操作対象を「画面上の見た目」ではなく「入出力を持つ処理」として扱います。この視点は、CI/CD、Docker、Kubernetes、クラウドCLI、ログ解析でもそのまま使えます。

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

多くのCLIツールは、3つの基本的な入出力を持ちます。

名前 番号 役割
標準入力(stdin) 0 入力を受け取る
標準出力(stdout) 1 通常の結果を出す
標準エラー(stderr) 2 エラーや診断情報を出す

標準出力と標準エラーを分ける理由は、自動化しやすくするためです。結果だけを次の処理へ渡し、エラーはログへ残す、といった構成ができます。

command > output.txt
command 2> error.log
command > output.txt 2> error.log

stdout はデータの流れ、stderr は人間や運用者への診断情報、と考えると整理しやすくなります。よいCLIツールは、この分離を守ることで、パイプラインの一部として使いやすくなります。

パイプラインで考える

パイプは、あるコマンドの出力を次のコマンドの入力へ渡します。

ps aux | grep node

パイプラインの考え方は、CLIの中心です。1つの巨大なコマンドを探すのではなく、小さなコマンドをつなげて目的の処理を作ります。

grep "ERROR" app.log | sort | uniq -c | sort -nr

この例は次の処理です。

  1. ERROR を含む行を抽出する
  2. 同じ行が隣り合うように並べる
  3. 重複行を数える
  4. 件数の多い順に並べる

Bashでは、pipelineの各コマンドは基本的に別プロセスで実行されます。また、pipeline全体のexit codeは、通常は最後のコマンドのexit codeになります。

grep "ERROR" missing.log | sort
echo "$?"

grep が失敗しても、最後の sort が成功すれば成功に見えることがあります。Bashやzshでは pipefail を使うと、途中の失敗を検知しやすくなります。

set -o pipefail
grep "ERROR" missing.log | sort

ただし、pipefail はPOSIX sh の標準ではありません。移植性を重視する場合は、途中結果をファイルや変数に分けて確認します。

パイプラインで行指向のストリームを渡す

パイプラインは、巨大なデータを一度に全部メモリへ載せる発想ではなく、行やチャンクを流しながら処理する発想と相性がよいです。もちろん sort のように入力全体を見ないと結果を出せないコマンドもあります。どの段階でデータが溜まるのかを意識すると、大きなログを扱うときに詰まりにくくなります。

sequenceDiagram participant A as grep participant B as sort participant C as uniq participant D as sort -nr A->>B: matched lines B->>C: sorted lines C->>D: counts D-->>A: final output is printed by shell pipeline

リダイレクトとファイルディスクリプタ

リダイレクトは、入力や出力の行き先を変える仕組みです。

echo "hello" > hello.txt
echo "world" >> hello.txt
wc -l < hello.txt

代表的なリダイレクトです。

書き方 意味
> 標準出力をファイルへ書く。既存内容は消える
>> 標準出力をファイルへ追記する
< ファイルを標準入力として渡す
2> 標準エラーをファイルへ書く
2>&1 標準エラーを標準出力と同じ先へ向ける
&> Bashで標準出力と標準エラーをまとめる

リダイレクトは左から右へ評価されます。順番が変わると意味も変わります。

command > out.log 2>&1

これは、標準出力を out.log に向けたあと、標準エラーをその標準出力と同じ先へ向けます。

command 2>&1 > out.log

こちらは、標準エラーを「その時点の標準出力」に向けたあと、標準出力だけを out.log に向けます。結果として、標準エラーは端末に残ることがあります。

here documentは、複数行の入力をコマンドへ渡すときに便利です。

cat <<'EOF'
hello
$HOME is not expanded
EOF

区切り語を引用すると、本文中の変数展開やコマンド置換を抑えられます。設定ファイルやテンプレートを生成するときに有用です。

リダイレクトを図にすると、次のようになります。

flowchart LR C["command"] --> O["fd 1 stdout"] C --> E["fd 2 stderr"] O --> OF["out.log"] E --> EF["error.log"]

command > out.log 2>&1 の場合は、標準エラーも標準出力と同じ先へ向きます。

flowchart LR C["command"] --> O["fd 1 stdout"] C --> E["fd 2 stderr"] O --> F["out.log"] E --> F

この図のように、リダイレクトは「コマンドの出力文字列をあとから置換する」機能ではなく、プロセスが使う入出力先を実行前に差し替える機能です。

exit codeと条件分岐

コマンドは終了時にexit codeを返します。一般に 0 は成功、0 以外は失敗です。

grep "ERROR" app.log
echo "$?"

条件分岐では、文字列の出力ではなくexit codeを見ます。

if grep -q "ERROR" app.log; then
  echo "error found"
else
  echo "ok"
fi

よく使う条件演算子です。

test -f README.md
[ -d dist ]
[ "$name" = "main" ]

[ はコマンドです。最後の ] も引数として必要です。空白を省略すると意図通りに動きません。

if [ -f README.md ]; then
  echo "exists"
fi

Bashでは [[ ... ]] も使えます。パターンマッチや一部の引用ルールが扱いやすくなりますが、POSIX sh では使えません。

if [[ "$branch" == feature/* ]]; then
  echo "feature branch"
fi

引用符と展開

CLIの事故の多くは、引用符と展開を理解していないことから起きます。

書き方 主な意味
'text' ほぼそのままの文字列として扱う
"text" 変数展開やコマンド置換は行うが、空白分割を抑える
\ 次の1文字の特別な意味を抑える
$name 変数を展開する
$(command) コマンドの出力を埋め込む
*.md 一致するファイル名へ展開する

原則として、変数展開は二重引用符で囲みます。

path="my file.txt"
cat "$path"

例外は、意図的に複数引数へ展開したい場合です。ただし、その場合も配列が使えるBashでは配列を使う方が安全です。

files=("README.md" "my file.txt")
wc -l "${files[@]}"

Bashの展開には、brace expansion、tilde expansion、parameter expansion、command substitution、arithmetic expansion、word splitting、filename expansionなどがあります。細部を全部覚える必要はありませんが、「引用しない変数は分割とglobの対象になる」と覚えておくと事故が減ります。

変数と環境変数

シェル変数は、現在のシェルの中で使う値です。

name="world"
echo "hello, $name"

環境変数は、子プロセスへ渡されるkey-valueの設定です。

export LOG_LEVEL=debug
npm run dev

1コマンドだけに環境変数を渡すこともできます。

LOG_LEVEL=debug npm run dev

代表的な環境変数です。

変数 意味
HOME ホームディレクトリ
PATH コマンド検索パス
PWD 現在のディレクトリ
SHELL ログインシェル
LANG ロケール
EDITOR 既定のエディタ

.env ファイルは便利ですが、秘密情報をGitに入れないようにします。API key、token、private keyは、履歴に残った時点で漏えいした前提で扱う方が安全です。

ファイルとディレクトリ操作

基本操作は少数です。まずは「状態を見る」「移動する」「作る」「コピーする」「消す」を区別します。

コマンド 用途
pwd 現在のディレクトリを表示
ls ファイル一覧
cd ディレクトリ移動
mkdir ディレクトリ作成
cp コピー
mv 移動・リネーム
rm 削除
touch 空ファイル作成・時刻更新
stat ファイル情報を表示
du ディスク使用量
df ファイルシステム使用量

削除系は特に注意します。

rm -rf dist

rm -rf は強力です。変数と組み合わせる場合は、空文字や想定外の値を防ぐために事前確認します。

target="${BUILD_DIR:-}"
if [ -z "$target" ]; then
  echo "BUILD_DIR is empty" >&2
  exit 1
fi
rm -rf "$target"

ファイル名が - から始まる場合、オプションと誤解されることがあります。-- を使うと、ここから先は引数だと明示できます。

rm -- "-dangerous-name"

検索と絞り込み

テキスト検索では grep、リポジトリ検索では rg がよく使われます。

grep -n "ERROR" app.log
grep -R "TODO" .
rg "TODO|FIXME"

よく使う grep オプションです。

オプション 意味
-n 行番号を出す
-i 大文字小文字を区別しない
-R 再帰的に検索
-E 拡張正規表現を使う
-v 一致しない行を出す
-q 出力せず、exit codeだけ返す

grep -q は条件分岐に向いています。

if grep -q "ERROR" app.log; then
  echo "error found"
fi

rg はGit ignoreを尊重し、高速に検索できるため、コードベースでは便利です。

rg -n "class User"
rg --files | rg "\\.md{{CONTENT}}quot;

findとxargs

find はファイル名、種類、更新時刻、サイズなどでファイルを探します。

find . -name "*.md"
find . -type f -name "*.js"
find . -type d -name node_modules -prune -o -type f -print

xargs は、標準入力をコマンドの引数に変換します。

find . -name "*.md" | xargs wc -l

ただし、この形は空白や改行を含むファイル名で壊れます。安全に扱うにはNUL区切りを使います。

find . -name "*.md" -print0 | xargs -0 wc -l

find -exec を使う方法もあります。

find . -name "*.md" -exec wc -l {} +

使い分けです。

方法 向いている場面
find ... -exec ... {} + ファイル名を安全に扱いたい
`find … -print0 xargs -0 …`
rg --files Git管理下のコード検索をしたい

テキスト処理の基本

CLIでは、テキストを行単位で処理することが多いです。代表的な道具です。

コマンド 用途
cat ファイル内容を表示
less 長いファイルを読む
head 先頭を見る
tail 末尾を見る
wc 行数・単語数・バイト数
sort 並べ替え
uniq 隣接する重複行をまとめる
cut 区切り位置や列を取り出す
tr 文字を置換・削除する
sed 行単位の置換や抽出
awk 列指向の処理や簡単な集計

ログ集計の例です。

awk '{print $1}' access.log | sort | uniq -c | sort -nr | head

CSVやJSONのような構造化データでは、無理に cutsed で壊れやすい処理をするより、専用ツールを使う方が安全です。

jq '.items[].name' data.json

テキスト処理は強力ですが、「行単位の単純な構造」を超えたらparserを使う、という判断が重要です。

プロセスとjob control

CLIは、プロセスを観察し、止め、再開する入口でもあります。

操作 意味
Ctrl-C foregroundのプロセスへ割り込みを送る
Ctrl-Z foregroundのプロセスを一時停止する
jobs 現在のjobを表示する
fg jobをforegroundに戻す
bg jobをbackgroundで再開する
command & commandをbackgroundで起動する
kill signalを送る

例です。

npm run build > build.log 2>&1 &
jobs
fg %1

プロセス確認には ps、リソース確認には tophtopポート確認には lsof などを使います。

ps aux | grep node
lsof -i :8000

プロセスを止めるときは、いきなり強制終了するより、まず通常の終了を試します。

kill "$pid"
kill -TERM "$pid"
kill -KILL "$pid"

SIGKILL はプロセスが後始末できないため、最後の手段です。

シェルスクリプトの基本形

繰り返す操作はスクリプトにできます。

#!/usr/bin/env sh
set -eu

echo "build..."
npm run build

echo "done"

引数を受け取る例です。

#!/usr/bin/env sh
set -eu

name="${1:-world}"
echo "hello, $name"

関数に分けると、読みやすくなります。

#!/usr/bin/env sh
set -eu

build() {
  npm run build
}

main() {
  build
}

main "$@"

Bashを前提にできるなら、配列や [[ ... ]]pipefail を使えます。

#!/usr/bin/env bash
set -euo pipefail

files=("README.md" "docs/index.md")
wc -l "${files[@]}"

堅牢なスクリプトを書く

set -euo pipefail はよく使われます。

設定 意味
set -e コマンド失敗時に終了しやすくする
set -u 未定義変数をエラーにする
set -o pipefail pipeline途中の失敗を検知しやすくする

ただし、これらは万能ではありません。特に set -e は、条件式、サブシェル、コマンド置換、pipelineと組み合わせたときに直感と違う挙動になることがあります。重要なところは明示的に検査します。

if ! npm run build; then
  echo "build failed" >&2
  exit 1
fi

一時ファイルを作る場合は、固定名ではなく mktemp を使います。

tmp="$(mktemp)"
trap 'rm -f "$tmp"' EXIT

printf '%s\n' "hello" > "$tmp"

trap は終了時の後始末に使えます。ビルド、変換、検証などで一時ファイルを使う場合に便利です。

引数の扱いも明示します。

usage() {
  echo "usage: $0 INPUT OUTPUT" >&2
}

if [ "$#" -ne 2 ]; then
  usage
  exit 2
fi

input="$1"
output="$2"

スクリプトを安全にする基本は、「入力を検査する」「変数を引用する」「失敗を見逃さない」「後始末を書く」です。

実践レシピ

CLIは、個別コマンドの知識よりも「目的から組み立てる力」が重要です。よくある目的と組み立て方を整理します。

目的 組み立て方
ファイルを探す find または rg --files find . -name "*.md"
内容を探す rg / grep `rg "TODO
行を絞る grep, awk grep "ERROR" app.log
列を取り出す awk, cut awk '{print $1}' access.log
件数を数える `sort uniq -c`
JSONを読む jq jq '.items[].name' data.json
繰り返し実行 xargs, loop `rg --files
結果を保存 redirect command > out.log 2>&1

ログから多いエラーを探す流れです。

flowchart LR A["app.log"] --> B["grep ERROR"] B --> C["awkでcomponentを抽出"] C --> D["sort"] D --> E["uniq -c"] E --> F["sort -nr"] F --> G["上位を見る"]
grep "ERROR" app.log \
  | awk '{print $3}' \
  | sort \
  | uniq -c \
  | sort -nr \
  | head

この例で重要なのは、各段階を単独で確認できることです。

grep "ERROR" app.log | head
grep "ERROR" app.log | awk '{print $3}' | head

pipelineが長くなったときは、一気に完成させず、左から少しずつ足します。これはデバッグのしやすさだけでなく、誤って大量のデータを削除・送信する事故を防ぐうえでも有効です。

ファイル名を安全に扱うレシピです。

find . -type f -name "*.md" -print0 \
  | xargs -0 wc -l

-print0-0 は、空白や改行を含むファイル名で壊れにくくするための組み合わせです。小さな個人用ディレクトリでは問題にならなくても、共有ディレクトリや外部から来たファイルを扱うなら意識します。

本番向け作業では、次の順で安全性を上げます。

flowchart TD A["対象を表示"] --> B["dry-run"] B --> C["件数・差分を確認"] C --> D["ログを残す"] D --> E["実行"] E --> F["exit codeと結果を確認"]

CLIの達人っぽさは、難しいone-linerを書くことではありません。危険な作業を、小さく確認可能な段階に分けられることです。

対話操作を速くする

CLIはスクリプトだけでなく、日々の操作でも力を発揮します。

操作
履歴検索 Ctrl-R
行頭へ移動 Ctrl-A
行末へ移動 Ctrl-E
1単語戻る Esc-b または Option-Left
1単語進む Esc-f または Option-Right
直前の引数 !$
直前のコマンド !!

aliasは短縮に便利です。

alias gs='git status --short'
alias ll='ls -la'

ただし、スクリプト内ではaliasに依存しない方が安全です。aliasは対話環境のためのものとして扱い、チームで共有する処理はscriptやMakefile、npm scriptsなどに寄せます。

安全に使うための注意

CLIは強力ですが、危険な操作も簡単にできます。

  • rm は基本的に元に戻せない
  • 変数展開は引用符で囲む
  • ワイルドカードの展開範囲を確認する
  • 管理者権限での実行は最小限にする
  • 知らないコマンドをそのまま貼り付けない
  • 秘密情報を履歴やログへ残さない
  • 本番環境ではdry-runや確認手順を用意する

危険な操作の前には、対象を表示します。

printf 'target: %s\n' "$target"

dry-runがあるコマンドでは、先にdry-runを使います。

rsync -av --dry-run dist/ server:/var/www/site/

履歴に残したくないsecretは、コマンドライン引数として直接渡さない方が安全です。引数は履歴やプロセス一覧に残ることがあります。環境変数、標準入力、secret managerなど、ツールが推奨する方法を使います。

UNIX哲学との接続

CLIはUNIX哲学を体験しやすい場所です。

  • 一つのコマンドが一つの役割を持つ
  • 標準入力と標準出力でつながる
  • テキストを介して観察・加工できる
  • パイプで小さな道具を組み合わせられる
  • スクリプトで繰り返しを自動化できる

CLIに慣れると、OS、ネットワーク、Git、CI/CD、ログ解析の理解も進みます。これは、どれも「入力を受け取り、処理し、出力し、失敗を伝える」という共通の形を持つからです。

標準的なPOSIX シェルコマンド (POSIX.1-2024)

コアコマンド一覧

文字列処理:
  cat         - ファイル出力
  echo        - 文字列出力
  grep        - 行検索
  sed         - ストリーム編集
  awk         - テキスト解析
  tr          - 文字変換
  sort        - ソート
  uniq        - 重複削除
  cut         - 列抽出
  paste       - 列結合

ファイル操作:
  ls          - ファイル一覧
  find        - ファイル検索
  cp          - コピー
  mv          - 移動
  rm          - 削除
  mkdir       - ディレクトリ作成
  chmod       - 権限変更
  chown       - 所有者変更
  stat        - ファイル情報

プロセス操作:
  ps          - プロセス一覧
  kill        - プロセス終了信号
  top/htop    - プロセスモニタ
  jobs        - バックグラウンドジョブ
  fg/bg       - フォアグラウンド/バックグラウンド
  nohup       - ハングアップ無視

ネットワーク:
  ping        - 到達確認
  netstat     - ネットワーク統計
  ss          - ソケット統計
  curl        - HTTP通信
  wget        - ファイル取得
  ssh         - リモートシェル
  scp         - セキュアコピー

パイプライン実装例

ログ解析の実務パターン:

# エラーログの時刻別集計
tail -f /var/log/app.log \
  | grep ERROR \
  | awk '{print $1}' \
  | uniq -c \
  | sort -rn \
  | head -10

各段階の役割:

  • tail -f: ファイルの最後をストリーム出力(新規追記監視)
  • grep ERROR: 「ERROR」を含む行に絞る
  • awk '{print $1}': 第1フィールド(タイムスタンプ)を抽出
  • uniq -c: 連続した同一行をカウント
  • sort -rn: 降順でソート
  • head -10: 上位10件を表示

exit code の正しい理解

POSIX.1-2024 では exit code が詳細に規定:

exit 0          # 成功
exit 1-125      # 汎用エラー (スクリプトが定義)
exit 126        # コマンド実行不可
exit 127        # コマンド見つからず
exit 128+N      # signal で終了 (N=信号番号)

例: プロセスが SIGKILL (9) で終了 → exit code は 137 (128+9)

実装例:

#!/bin/bash
set -e  # error で即座に終了

process_file() {
  if [[ ! -f "$1" ]]; then
    echo "file not found: $1" >&2
    return 1
  fi
  # 処理
}

process_file "$input_file" || {
  echo "failed to process" >&2
  exit 1
}

シェルスクリプト設計

スクリプト先頭の推奨設定

#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

# e: error で終了
# u: 未定義変数は error
# o pipefail: pipe内のいずれかが失敗したら失敗
# IFS: 単語分割の設定

readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

変数と引用符

危険なパターン:

# 悪い例: 変数展開時に空白で分割される
rm -r $target_dir/*

安全なパターン:

# 良い例: ダブルクォートで保護
rm -r "$target_dir"/*

# より安全: find + xargs -0
find "$target_dir" -type f -print0 | xargs -0 rm

関数のテンプレート

log_info() {
  echo "[INFO] $*" >&2
}

log_error() {
  echo "[ERROR] $*" >&2
}

main() {
  log_info "Starting process"
  
  if ! command -v required_cmd &>/dev/null; then
    log_error "required_cmd not found"
    return 1
  fi
  
  # 処理
}

main "$@"

コマンドライン引数処理(getopt/getopts)

POSIX 準拠の getopt:

#!/bin/bash

while getopts "hi:o:v" opt; do
  case "$opt" in
    h)
      echo "Usage: $0 -i input -o output [-v]"
      exit 0
      ;;
    i)
      input_file="$OPTARG"
      ;;
    o)
      output_file="$OPTARG"
      ;;
    v)
      verbose=true
      ;;
    *)
      echo "invalid option: -$OPTARG" >&2
      exit 1
      ;;
  esac
done
shift $((OPTIND - 1))

[[ -n "${input_file:-}" ]] || { echo "input file required"; exit 1; }

環境変数とセキュリティ

PASSWORD や API KEY を渡す場合:

# 悪い例: コマンドラインに見える
myapp --password "secret123"

# 良い例: 環境変数、`.env` ファイル、secret manager を使用
export DB_PASSWORD="secret123"
source .env
myapp

.env ファイル をセキュアに読み込む:

if [[ -f .env ]]; then
  set -a
  source .env
  set +a
fi

まとめ

CLIは、コマンド暗記の技術ではなく、処理を小さな部品へ分け、標準入出力でつなぎ、失敗をexit codeで扱い、再現可能な形にするための技術です。

最初に押さえるべきものは、標準入力、標準出力、標準エラー、パイプ、リダイレクト、引用符、変数、exit codeです。これらを理解すると、個々のコマンドは入れ替え可能な道具として見えるようになります。

参考文献

公式・標準

解説・補助