Linux 講座 / シェルのコマンド実行方式 - ;, &&, ||, エイリアスと環境変数
Bashはコマンドを読み、名前と引数を解析し、必要な展開・リダイレクトを適用した後に実行します。;は前のコマンドの成功・失敗に関わらず次のコマンドを、&&は成功した場合のみ、||は失敗した場合のみ次のコマンドを実行します。エイリアスは読み込む時点でコマンドを置き換え、環境変数は子プロセスに渡される設定です。この違いを理解すれば、同じ文字を入力しても結果が変わる理由を説明できます。
最初は変数・環境変数・エイリアスと終了ステータス、&&・||の基本の流れを覚えれば十分です。コマンドのグループ化・パイプライン・バックグラウンド実行や全体的なスクリプトの例は必要な時に選んで読みましょう。実行原理をファイルにまとめる方法は最初のBashスクリプト、失敗時の処理の限界やファイル操作の安全性はBashのエラー処理で拡張説明します。
コマンド名はどのように決定されるのでしょうか?
この講座の例はBashを基準としています。シェルはコマンドラインを読み、引数・変数・リダイレクトを処理した後にコマンド名を解釈します。名前がエイリアス、関数、シェル組み込みコマンドか、それともPATHで見つけた実行ファイルかによって、実際の動作は異なる場合があります。現在のシェルの解釈を確認するには、次を実行してください。
type -a printf
type -a ls
command -v ls
type -aの出力は、エイリアス・関数・組み込みコマンド・実行ファイルの候補を表示できます。command -vは、コマンド名を見つけられるか確認するときに使用できます。PATHは外部実行ファイルを探すディレクトリの一覧であり、エイリアスや関数までファイルパス一つで説明することはできません。システムのPATHを練習のために任意に上書きしないでください。
終了状態と実行記号を一目で確認
コマンドが終了すると、シェルは終了状態(exit status)を受け取ります。0は成功を意味し、0以外の値は失敗やそのコマンドが定義した他の結果を意味します。例えば、grepは一致する行がなければ1を返すことがあります。$?は直近に実行したコマンドの終了状態なので、すぐに確認するか変数に保存する必要があります。
true
printf 'status=%s\n' "$?"
false
printf 'status=%s\n' "$?"
順にstatus=0、status=1が出力されます。画面上に成功のように見える文章が出たかどうかではなく、終了状態が条件付き実行を決定します。Bashがコマンドを見つけられない場合は127を、見つけても実行できない場合は126を返し、シグナルで終了した場合には別の状態規則を使用します。すべての失敗が1ではありません。
| 記号 | 役割 |
|---|---|
; |
成功・失敗に関係なく次のコマンドを実行 |
&& |
成功した場合にのみ次のコマンドを実行 |
|| |
0でない状態の場合に次のコマンドを実行 |
& |
完了を待たずにバックグラウンドで実行 |
| |
標準出力を次のコマンドの標準入力に接続 |
|& |
標準出力と標準エラーを一緒にパイプで渡す |
! |
コマンド・パイプラインの終了状態を論理的に反転 |
Bashのコマンドリストのドキュメントでは、;、&、&&、||はコマンドリストを構成する演算子です。|と|&はパイプライン演算子であり、!はパイプラインの終了状態を反転する予約語です。この記事では、Bashターミナルでtrue、false、printf、grep、cat、sleepを使用します。Ubuntu・Rocky Linuxの一般的な環境で提供されるコマンドで、管理者権限は必要ありません。
成功と失敗に応じてコマンドを接続する
セミコロンと条件付き実行の違い
;は、前のコマンドが終了すると結果に関係なく次のコマンドを実行します。一般的なコマンドリストでは改行も同じ区切りの役割を果たします。ただし、引用符・バックスラッシュ・パイプ・複合コマンドによって入力がまだ完了していない場合、改行はコマンドを終了させないことがあります。
false; printf 'always\n'
false && printf 'not run\n'
false || printf 'fallback\n'
true && printf 'next step\n'
always、fallback、next stepが順番に出力され、not runは出力されません。&&と||は、実行条件が満たされない場合、右側のコマンドをスキップするショートサーキット評価を行います。例えば、cd /実際/作業/パス && 作業命令は、ディレクトリ移動が失敗したときに他の場所での作業が実行されるのを防ぎます。この表記は、実際のパスと準備された作業コマンドに置き換えて適用するパターンです。
評価順序とif分岐の区別
&&と||は優先順位が同じで、左から結合されます。したがって、A && B || CでAが成功してもBが失敗すればCが実行されます。
true && false || printf 'fallback runs\n'
fallback runsが出力されます。最初の条件の結果に応じて二分岐を選択するだけの場合は、次のようにifを使います。
if true; then
false
else
printf 'condition failed\n'
fi
上の例では、then内のfalseが失敗してもelseには進まず、出力はありません。ただし、実行した分岐の失敗が自動的に回復するわけではありません。運用スクリプトでは、各段階の失敗を確認し、必要な終了状態を返す必要があります。
エラーメッセージの出力と失敗状態の保持
false || printf 'failure reported\n'
printf 'list status=%s\n' "$?"
failure reported の後に list status=0 が出力されます。 || はエラーを無条件に無視する記号ではありませんが、最後に実行した printf が成功したため、リストの最終状態が 0 になっています。エラーを通知して失敗で終了する必要がある場合は、元の状態を保存した後、スクリプトで exit を使って元の状態を返す方式が必要です。
複数のコマンドを1つの作業としてまとめる
中括弧は現在のシェルで実行
{ ...; } は現在のシェルで複数のコマンドを実行します。中括弧とコマンドの間にはスペースが必要で、閉じ中括弧の前にはセミコロンか改行が必要です。
LESSON_SCOPE=outer
{ LESSON_SCOPE=inner; printf '%s\n' "$LESSON_SCOPE"; }
printf '%s\n' "$LESSON_SCOPE"
unset LESSON_SCOPE
inner が2回出力されます。グループ内で変更した変数が現在のシェルにも残るためです。練習用の変数は最後に unset で削除します。
小括弧はサブシェルで実行
LESSON_SCOPE=outer
( LESSON_SCOPE=inner; printf '%s\n' "$LESSON_SCOPE" )
printf '%s\n' "$LESSON_SCOPE"
unset LESSON_SCOPE
inner、outerが順番に出力されます。小括弧は別のシェル実行環境であるサブシェル(subshell)を作るため、通常の変数や現在のディレクトリの変更は外側のシェルに残りません。ファイル作成・削除や外部サービスへのリクエストのような外部効果まで元に戻すわけではありません。Bash コマンドのグループ化に関するドキュメントで二つのグループの違いを確認できます。
パイプ・標準エラー・終了ステータスの反転
パイプと条件付き実行を区別する
printf 'alpha\nbeta\n' | grep 'alpha'
alphaのみが出力されます。|は前のコマンドの標準出力(standard output)を次のコマンドの標準入力(standard input)に接続します。前のコマンドが失敗したときに次のコマンドを実行する||とは役割が異なります。パイプライン内のコマンドは前のコマンドの成功を待って選択的に実行されるわけではなく、データを受け継いで処理します。
標準エラーも伝達する |&
bash -c 'printf "normal\n"; printf "error\n" >&2' |& cat
normalとerrorはどちらもcatを経由して出力されます。Bashの|&はこの例では2>&1 |のように標準エラー(standard error)を標準出力に統合して渡します。既存のリダイレクションが複数ある場合は適用順序も重要になるため、単純に記号だけを置き換えないでください。画面出力とログを一緒に残す場合は、次のコマンドでteeを使用できますが、正常出力とエラーが統合される点に注意してください。
!は出力ではなく終了ステータスを反転
! false
printf 'inverted status=%s\n' "$?"
inverted status=0が出力されます。本来失敗状態であるfalseが条件チェック上成功に変わったものです。本来の状態が0の場合、!は0でない状態に変わります。エラーメッセージやファイル内容を逆にする機能ではありません。
pipefailでパイプラインの失敗確認
( set +o pipefail; false | cat; printf 'default=%s\n' "$?" )
( set -o pipefail; false | cat; printf 'pipefail=%s\n' "$?" )
default=0, pipefail=1が出力されます。基本的にパイプラインは最後のコマンドの状態を返すため、前のfalseが失敗してもcatが成功すれば全体の状態は0です。pipefailを有効にすると、すべて成功した場合のみ0であり、失敗があれば、失敗したコマンドのうち最も右側にあるコマンドの終了状態が使われます。各比較は括弧内で実行されたため、既存のシェルのオプションは変わりません。Bashパイプラインのドキュメントがこのルールを説明しています。
バックグラウンド実行とジョブ完了確認
&はコマンドを非同期で実行し、完了を待たないようにします。成功した場合にのみ次のステップに進む&&とは区別する必要があります。以下の例では、短時間待つ作業を実行した後、任意のタイミングでwaitでその作業の終了状態を確認します。
sleep 2 &
lesson_job=$!
printf 'foreground continues\n'
wait "$lesson_job"
printf 'job status=%s\n' "$?"
unset lesson_job
foreground continuesが最初に出力され、作業が終了するとjob status=0が出力されます。$!は最近のバックグラウンド作業のプロセスIDであり、実行直後の$?だけでバックグラウンド作業が正常に終了したと判断してはいけません。対話型Bashのjobs、fg、bgは作業の確認やフォアグラウンド・バックグラウンドの切り替えに使用します。&だけを付けてもログアウト後の実行は保証されないため、長期作業には適切なセッション管理やサービス管理の方法を選んでください。
成功・失敗の流れをスクリプト全体で実習する
以下はビルド・テスト・デプロイの実行順序をメッセージだけで模倣する全体例です。実際のビルドやデプロイ、ファイル削除は行いません。空の練習ディレクトリで内容をcourse15-flow.shという新しいファイルに保存してください。実行にはBashだけが必要で、実行権限を追加する必要はありません。
#!/usr/bin/env bash
build() { printf 'build\n'; }
run_tests() { printf 'test\n'; return "${1:-0}"; }
deploy() { printf 'deploy\n'; }
build && run_tests "${1:-0}" && deploy
lesson_status=$?
if (( lesson_status != 0 )); then
printf 'workflow failed: status=%s\n' "$lesson_status" >&2
fi
exit "$lesson_status"
bash course15-flow.sh 0
echo $?
bash course15-flow.sh 1
echo $?
最初の実行ではbuild・test・deployを出力し、状態は0です。引数を1に変更すると、build・testまでしか実行せず、標準エラーに失敗メッセージを出力し、状態は1です。再び0で実行すると成功フローに戻ります。${1:-0}は、最初の引数がない場合や空の場合に0を使う表現で、この演習では引数として0または1のみを使用してください。
関数は名前() { ...; }でコマンドの塊に名前を付け、returnで終了状態を返します。例では実行直後、状態をlesson_statusに保存するため、メッセージを出力しても元の失敗は消えません。実際の運用では、各関数の代わりに準備・検証された作業を使用し、ステップごとの失敗処理を明確に記述してください。
短い独立した作業にはセミコロン、前のステップの成功が必要な作業には&&、複数の分岐とリカバリが必要な作業にはif·関数を選択します。作業; 整理命令は作業が失敗しても後続を実行しますが、シェルが途中で終了したりシグナルを受ける状況まで整理実行を保証するものではありません。このような整理はtrapなどで設計すべきであり、set -eだけですべての失敗が自動処理されると考えないでください。条件式と&&·||のリストには例外があるため、終了状態を明示的に確認する習慣が重要です。
エイリアスはシェルが読むときに展開されます
aliasは対話型Bashでよく使うコマンドに短い名前を作ります。次の行を入力した後、新しいプロンプトでllまたはtype llを実行してみてください。Bashはコマンドラインを実行する前に読み取り解析するため、エイリアス定義と使用を1行の複合コマンドにまとめると期待通りに展開されないことがあります。
alias ll='ls -l'
type ll
ll
エイリアスは基本的に現在のシェルにのみ適用され、非対話型Bashでは別途設定しない限り展開されません。実際の自動化スクリプトがエイリアスに依存すると考えないでください。演習が終わったらunalias llで削除できます。引数を受け取って再利用する動作は、エイリアスよりもシェル関数を検討するほうがよいでしょう。
シェル変数と環境変数の区別
シェル変数は現在のシェルで使用できますが、exportした変数は、その後に実行される子プロセスの環境にも反映されます。以下の例では、よくあるシステム変数の代わりに、演習用の名前を使用しています。
LESSON_MODE=practice
printf '%s\n' "$LESSON_MODE"
export LESSON_MODE
printenv LESSON_MODE
両方の出力は practice です。最初の出力は現在のシェルでの変数展開の結果で、2つ目の出力は外部 printenv プロセスが受け取った環境を読み取った結果です。export はすでに実行中の他のシェルや親プロセスを逆に変更することはありません。
1つのコマンドだけに値を渡したい場合は、名前=値 コマンド 形式を使うことができます。演習用の変数を整理する際は、現在のシェルで unset LESSON_MODE を実行してください。ユーザーのホームディレクトリやシステムの PATH などの重要な変数は、演習用の名前として再利用しないでください。
実行順序を確認する方法
- コマンドが何として解釈されるかわからない場合は、
type -a 名前から確認します。 - 条件付き実行が予期と異なる場合は、各ステップの終了状態を分けて確認します。
- エイリアスが動作しない場合は、対話型シェルかどうか、定義後に新しいコマンドラインで実行したかを確認します。
- 子プロセスのコマンドが変数を読み取れない場合は、
exportの有無を確認します。
セミコロンと条件演算子、エイリアスと変数はすべて「コマンド実行方式」に関与しますが、同じ機能ではありません。実際のファイルを変更するコマンドを接続する前に、まず true、false、printf のような副作用が少ないコマンドでフローを確認してください。
公式ドキュメント
GNU Bash コマンド一覧ドキュメントでは、;・&&・|| の結合規則を説明しており、Bash エイリアスドキュメントでは展開のタイミングを説明しています。変数と環境は Bash リファレンスマニュアルで確認できます。
パイプラインと pipefail および コマンドのグループ化、終了ステータス の詳細な規則は、GNU Bash の公式ドキュメントで確認できます。




