インフラエンジニアの羅針盤

インフラエンジニア1〜3年目のための技術ガイド

Bashシェルスクリプト入門:特殊変数・if/for構文・test演算子と終了ステータス【LinuC102】

公開
,
LinuC Level 1 第11回 主題1.09 シェルおよびシェルスクリプト



Linuxにおける定常的なバックアップ、ログの定期巡回、ユーザーの一括作成といった運用保守業務は、シェルスクリプトによって自動化されます。LinuC Level 1の全39トピック中、唯一となる最高重要度「重要度6」が設定されているのが主題1.06.2「簡単なスクリプトのカスタマイズまたは作成」です。本稿では、シバンの意味から位置パラメータ($0$9$#$@)、終了ステータス判定($?)、testおよび大括弧([ ])による条件式、そしてifcaseforwhileuntilといった制御構文の完全な記法を、実機検証ログとともに論理的に解説します。

  • シバン(#!/bin/bash)の宣言と実行権限(chmod +x)の設定手順を説明できる状態を目指します
  • 位置パラメータ($0$9${10})と特殊変数($#$@$*$?$$)の役割および挙動差を完全に把握します
  • testコマンドおよび大括弧([ ])を用いたファイル判定(-f, -d, -s)、文字列比較(-z, -n, =)、数値比較(-eq, -lt, -ge等)を正確に記述できるようにします
  • Bashの主要制御構文(ifficaseesacfordonewhile/untildone)とreadexitの文法を習得します
  • 実機スクリプトを作成・実行し、終了コードをハンドリングできる実践力を定着させます
項目 仕様・設定値
検証機ホスト名 linuc-node01
ディストリビューション AlmaLinux 9.8 (Olive Jaguar)
シェル環境 GNU bash, バージョン 5.1.8(1)-release
インタープリタパス /bin/bash(/usr/bin/bashへのシンボリックリンク)
対象試験 LinuC レベル1 102試験(主題1.06.2 最重要度6)
目次
  1. 1. シェルスクリプトの基礎構造と実行手順
    1. 1.1 シバン(Shebang: #!/bin/bash)の役割と指定方法
    2. 1.2 実行権限の付与(chmod +x)と実行方法の差異
    3. 1.3 コメントアウトと可読性の確保
    4. 1.4 スクリプトのデバッグ手法(bash -n と bash -x)
  2. 2. 位置パラメータと特殊変数の完全整理
    1. 2.1 スクリプト名と引数($0, $1〜$9, ${10}〜)
    2. 2.2 引数の総数($#)と全引数の展開($@ vs $*)
    3. 2.3 直前コマンドの終了ステータス($?)とプロセスID($$)
    4. 2.4 shiftコマンドによる引数のシフト処理
  3. 3. testコマンドと大括弧 [ ] による条件判定
    1. 3.1 testコマンドと [ ] 構文の正体
    2. 3.2 ファイル検査演算子(-e, -f, -d, -s, -r, -w, -x, -L)
    3. 3.3 文字列比較演算子(-z, -n, =, !=)
    4. 3.4 数値比較演算子(-eq, -ne, -lt, -le, -gt, -ge)
    5. 3.5 論理演算子による結合(-a, -o, !, &&, ||)
  4. 4. Bashの制御構造(分岐・反復・対話入力)
    1. 4.1 if文による条件分岐
    2. 4.2 case文によるパターンマッチング
    3. 4.3 for文によるリスト反復処理とseqコマンドの連携
    4. 4.4 while文とuntil文によるループ処理の相違
    5. 4.5 readコマンドによる対話型ユーザー入力の受付
    6. 4.6 exitコマンドによるスクリプトの明示的終了と終了ステータス返却
  5. 5. 実機ハンズオン演習:AlmaLinux 9で実践的スクリプトを構築
    1. 5.1 実機検証環境の確認
    2. 5.2 演習1:引数検証と終了ステータス判定スクリプトの作成
    3. 5.3 演習2:ファイル存在チェックとバックアップ自動化スクリプト
    4. 5.4 演習3:for文とseqを用いた連番処理と実行ログ出力
  6. 6. LinuC 102試験対策まとめとコマ問頻出チェック
    1. 6.1 特殊変数・比較演算子完全対比マトリクス
    2. 6.2 コマ問(記述式)頻出キーワード一覧表
    3. 6.3 Ping-t演習へのブリッジ(対象分野と推奨学習順)
  7. 7. まとめと次回予告

1. シェルスクリプトの基礎構造と実行手順

シェルスクリプトとは、ターミナル上で対話的に実行するコマンド群をテキストファイルに書き連ね、上から下へと逐次実行させるプログラムです。

Bash位置パラメータと特殊変数の完全対応マップ
図1: Bash 位置パラメータと特殊変数の完全対応マップ

1.1 シバン(Shebang: #!/bin/bash)の役割と指定方法

スクリプトの第1行目に記述する #! で始まる特別な行をシバン(Shebang / シェバング)と呼びます。

#!/bin/bash

カーネルは実行権限が付与されたスクリプトを起動する際、このシバン行を参照して「どのインタープリタにスクリプトを解釈させるか」を決定します。もしシバンを省略した場合、現在ユーザーが起動しているカレントシェルで実行されますが、Bourne Shell(sh)やZshなどの環境差異によって意図しない動作不良を招く危険性があります。そのため、Bashスクリプトを作成する際は必ず第1行目に#!/bin/bash(または可搬性を重視した#!/usr/bin/env bash)を明記することが標準です。

1.2 実行権限の付与(chmod +x)と実行方法の差異

作成したスクリプトファイルを実行するには、適切なパーミッションが必要です。

  • 実行権限を付与して直接実行する手法(推奨標準)

    [user01@linuc-node01 ~]$ chmod +x myscript.sh
    [user01@linuc-node01 ~]$ ./myscript.sh

    カレントディレクトリを表す ./ を先頭に付与します。PATH環境変数に含まれないディレクトリにある実行可能ファイルは、相対パスまたは絶対パスで指定しなければ起動できません。

  • シェルコマンドの引数として渡す手法

    [user01@linuc-node01 ~]$ bash myscript.sh

    この場合、ファイル自体の実行権限(x)がなくても、読み取り権限(r)さえあれば実行可能です。

1.3 コメントアウトと可読性の確保

スクリプト内において、先頭に # 記号が置かれた行(シバンを除く)はコメント行として扱われ、インタープリタによる解釈がスキップされます。

1.4 スクリプトのデバッグ手法(bash -n と bash -x)

シェルスクリプトの不具合調査や構文チェックには、Bashのデバッグオプションが広く活用されます。LinuC試験でも出題される重要知識です。

  • 構文チェック(ドライラン): bash -n <スクリプト名>
    スクリプトを実際に実行することなく、文法エラー(iffor の構文不備、クォートの閉じ忘れなど)の有無のみを静的に検査します。運用中の環境で誤作動を起こさずに安全にスクリプトを事前検証する際に重宝します。
  • 実行トレース(デバッグ表示): bash -x <スクリプト名>
    実行された各行のコマンドと展開後の変数の値を、先頭に + 記号を付与して標準エラー出力に順次表示しながら処理を進めます。スクリプト内の特定区間のみをトレースしたい場合は、コード中に set -x(トレース開始)と set +x(トレース終了)を埋め込んで制御することも可能です。

2. 位置パラメータと特殊変数の完全整理

スクリプト起動時に引数を受け取ったり、直前の処理結果を判定したりするための特殊な変数を「位置パラメータ」および「特殊変数」と呼びます。LinuC 102試験における最大の出題領域です。

2.1 スクリプト名と引数($0, $1〜$9, ${10}〜)

コマンドラインで渡された引数は、順序に応じて以下の変数に自動格納されます。

  • $0: 実行されたスクリプト自体の名前(パス)
  • $1: 第1引数
  • $2: 第2引数(以降 $9 まで同様)
  • ${10}以降: 10番目以降の引数は必ず波括弧({ })で囲む必要があります。単に $10 と記述すると、「$1 の値に数字の 0 を連結した文字列」として解釈されてしまうためです。

2.2 引数の総数($#)と全引数の展開($@ vs $*)

渡された引数の数や全体を一括で扱うための特殊変数です。

特殊変数 格納される内容・動作 具体例(./script.sh "a b" c の場合)
$# 渡された引数の総数(整数値) 2
$@ 全引数のリスト(空白区切り) a b c
"$@" 全引数を個別のクォート文字列として展開(推奨) "a b" "c"(2つの引数として正確に維持される)
"$*" 全引数を単一の文字列として結合して展開 "a b c"(1つの長い文字列に合体する)

引数をループで1つずつ処理する際は、空白を含む引数が分割されないよう、必ず for arg in "$@" とダブルクォートで囲んで記述します。

2.3 直前コマンドの終了ステータス($?)とプロセスID($$)

  • $?: 直前に実行されたコマンドの終了ステータス(終了コード)を保持します。UNIX/Linuxの標準仕様では、0 は「成功(正常終了)」1〜255 は「エラー(異常終了)」を意味します。
  • $$: 現在実行しているスクリプト(シェル)自身のプロセスID(PID)です。一時ファイル名に一意性を付与する際(例: /tmp/work_$$.tmp)に頻用されます。
  • $!: 直前にバックグラウンド(&)で実行開始されたプロセスのPIDです。

2.4 shiftコマンドによる引数のシフト処理

shift コマンドを実行すると、位置パラメータ全体が左へ1つ移動します。すなわち、$1 が破棄され、$2 の内容が新たな $1 となり、引数の総数 $# も1つ減算されます。引数の個数が不定のスクリプトをループ処理する際に有用です。

3. testコマンドと大括弧 [ ] による条件判定

条件分岐(if文)の判定基準として用いられるのが test コマンドです。

Bash条件判定と制御構文完全マップ
図2: Bash 条件判定(test / [ ])と制御構文完全マップ

3.1 testコマンドと [ ] 構文の正体

Bashにおいて、[ ... ] は単なる記号ではなく、test コマンドのエイリアス(実体は内部コマンドまたは /usr/bin/[です。末尾の ] は、[ コマンドに対する必須の「最終引数」として機能します。

したがって、大括弧の内側には必ず半角スペースを空けなければなりません

  • 正しい記述: if [ -f "$file" ]; then[ の後、および ] の前にスペースがある)
  • 構文エラー: if [-f "$file"]; then(「[-f」という未知のコマンドとして認識されて失敗する)

3.2 ファイル検査演算子(-e, -f, -d, -s, -r, -w, -x, -L)

指定したパスのファイル属性を検査する演算子です。

演算子 判定条件(真となる条件) 実務・試験での重要度
-e file ファイルまたはディレクトリが存在する(Exist) 頻出
-f file 通常ファイルとして存在する(ディレクトリやデバイスは除外) 最頻出
-d file ディレクトリとして存在する 最頻出
-s file ファイルが存在し、サイズが0より大きい(空ファイルでない) 頻出
-r file 実行ユーザーに対して読み取り権限(Read)がある 頻出
-w file 実行ユーザーに対して書き込み権限(Write)がある 頻出
-x file 実行ユーザーに対して実行権限(Execute)がある 頻出
-L file シンボリックリンクである 頻出

3.3 文字列比較演算子(-z, -n, =, !=)

文字列の長さや一致を比較する演算子です。

  • -z "$str": 文字列の長さがゼロ(空文字: Zero)であれば真
  • -n "$str": 文字列の長さがゼロより大きい(空でない: Non-zero)であれば真
  • "$s1" = "$s2"(または ==): 2つの文字列が完全一致すれば真
  • "$s1" != "$s2": 2つの文字列が異なっていれば真

3.4 数値比較演算子(-eq, -ne, -lt, -le, -gt, -ge)

整数値を比較する際は、不等号記号(<>)ではなく英字のオプションを用います。不等号記号を使うとファイルリダイレクトと誤認されるためです。

演算子 英語の語源 意味・条件
$a -eq $b Equal $a$b が等しい
$a -ne $b Not Equal $a$b が等しくない
$a -gt $b Greater Than $a$b より大きい(超過)
$a -ge $b Greater than or Equal $a$b 以上
$a -lt $b Less Than $a$b 未満
$a -le $b Less than or Equal $a$b 以下

3.5 論理演算子による結合(-a, -o, !, &&, ||)

  • ! 条件: 条件の否定(NOT)
  • [ 条件1 -a 条件2 ]: testコマンド内での論理積(AND)
  • [ 条件1 -o 条件2 ]: testコマンド内での論理和(OR)
  • [ 条件1 ] && [ 条件2 ]: コマンドレベルでのAND結合(推奨)
  • [ 条件1 ] || [ 条件2 ]: コマンドレベルでのOR結合(推奨)

4. Bashの制御構造(分岐・反復・対話入力)

条件に応じて処理を切り替えたり、同じ処理を繰り返したりするための制御構造です。

4.1 if文による条件分岐

条件式の真偽(終了コードが0か否か)に応じて実行経路を分岐します。fi で閉じる構文規則が特徴的です。

if [ $# -eq 0 ]; then
    echo "引数が指定されていません"
elif [ $# -eq 1 ]; then
    echo "引数が1個指定されました: $1"
else
    echo "引数が複数指定されました: $#"
fi

4.2 case文によるパターンマッチング

値が複数のパターンのどれに合致するかを判定します。各分岐の終端は ;;(セミコロン2つ)で区切り、全体を esac(caseの逆綴り)で閉じます。

case "$1" in
    start)
        echo "サービスを開始します"
        ;;
    stop)
        echo "サービスを停止します"
        ;;
    status)
        echo "状態を表示します"
        ;;
    *)
        echo "使用方法: $0 {start|stop|status}"
        exit 1
        ;;
esac

4.3 for文によるリスト反復処理とseqコマンドの連携

指定した要素リストを1つずつ変数に取り出してブロックを反復実行します。開始は do、終端は done です。

# 単語リストの反復
for server in web01 web02 db01; do
    echo "Ping送信先: $server"
done

# seqコマンドによる連番ループ(1から5まで)
for i in $(seq 1 5); do
    echo "処理カウント: $i"
done

4.4 while文とuntil文によるループ処理の相違

  • while [ 条件 ]; do ... done: 条件が「真(終了ステータス0)」である間、ループを継続します。
  • until [ 条件 ]; do ... done: 条件が「偽(終了ステータス非0)」である間、ループを継続します(条件が真になった瞬間にループを抜けます)。

4.5 readコマンドによる対話型ユーザー入力の受付

キーボードからの入力を受け取って変数に代入するには read コマンドを用います。-p オプションでプロンプト文字列を指定できます。

read -p "続行しますか? (y/n): " answer
if [ "$answer" = "y" ]; then
    echo "処理を開始します"
fi

4.6 exitコマンドによるスクリプトの明示的終了と終了ステータス返却

スクリプトの実行を途中で中断・終了させるには exit コマンドを実行します。

  • exit 0: 正常終了(成功ステータス0を呼び出し元へ返却)
  • exit 1(または任意の非0数値): 異常終了(エラーコードを返却)

5. 実機ハンズオン演習:AlmaLinux 9で実践的スクリプトを構築

実機linuc-node01において、位置パラメータの検証と終了ステータス制御を伴う実践的なスクリプトを作成・検証します。

5.1 実機検証環境の確認

Bashの実行環境と作業ディレクトリを準備します。

[user01@linuc-node01 ~]$ which bash
/usr/bin/bash

[user01@linuc-node01 ~]$ mkdir -p ~/script_test && cd ~/script_test

5.2 演習1:引数検証と終了ステータス判定スクリプトの作成

引数の個数を検査し、不足している場合は利用方法を表示してエラー終了(exit 1)するスクリプトを作成します。

# スクリプト作成
[user01@linuc-node01 script_test]$ cat << 'EOF' > check_args.sh
#!/bin/bash

echo "スクリプト名: $0"
echo "引数の総数: $#"

if [ $# -lt 1 ]; then
    echo "エラー: 引数が指定されていません" >&2
    echo "使用法: $0 <引数1> [引数2...]" >&2
    exit 1
fi

echo "処理対象引数一覧: $@"
for arg in "$@"; do
    echo "  - パラメータ: $arg"
done

exit 0
EOF

# 実行権限の付与
[user01@linuc-node01 script_test]$ chmod +x check_args.sh

実行結果と終了コードの確認:

# 引数なしで実行(異常終了テスト)
[user01@linuc-node01 script_test]$ ./check_args.sh
スクリプト名: ./check_args.sh
引数の総数: 0
エラー: 引数が指定されていません
使用法: ./check_args.sh <引数1> [引数2...]
[user01@linuc-node01 script_test]$ echo $?
1

# 引数を渡して実行(正常終了テスト)
[user01@linuc-node01 script_test]$ ./check_args.sh Alice Bob Charlie
スクリプト名: ./check_args.sh
引数の総数: 3
処理対象引数一覧: Alice Bob Charlie
  - パラメータ: Alice
  - パラメータ: Bob
  - パラメータ: Charlie
[user01@linuc-node01 script_test]$ echo $?
0

5.3 演習2:ファイル存在チェックとバックアップ自動化スクリプト

指定したファイルが存在するか判定し、存在する場合のみバックアップを作成するスクリプトを作成します。

[user01@linuc-node01 script_test]$ cat << 'EOF' > backup_file.sh
#!/bin/bash

TARGET="$1"

if [ -z "$TARGET" ]; then
    echo "エラー: ファイル名を指定してください"
    exit 1
fi

if [ -f "$TARGET" ]; then
    BACKUP="${TARGET}.bak.$(date +%Y%m%d)"
    cp "$TARGET" "$BACKUP"
    echo "バックアップを作成しました: $BACKUP"
    exit 0
else
    echo "エラー: $TARGET は通常ファイルとして存在しません"
    exit 2
fi
EOF

[user01@linuc-node01 script_test]$ chmod +x backup_file.sh

5.4 演習3:for文とseqを用いた連番処理と実行ログ出力

seq コマンドを活用して連番のディレクトリを一括生成します。

[user01@linuc-node01 script_test]$ for i in $(seq 1 3); do
>     DIR="backup_dir_0${i}"
>     mkdir -p "$DIR"
>     echo "作成完了: $DIR"
> done
作成完了: backup_dir_01
作成完了: backup_dir_02
作成完了: backup_dir_03

# 検証後のクリーンアップ
[user01@linuc-node01 script_test]$ cd ~ && rm -rf ~/script_test

6. LinuC 102試験対策まとめとコマ問頻出チェック

主題1.06.2は重要度6にふさわしく、文法記号の暗記がそのまま得点に直結します。

6.1 特殊変数・比較演算子完全対比マトリクス

記号・演算子 分類 機能・意味
$0 特殊変数 実行されたスクリプト自身のファイル名
$# 特殊変数 スクリプトに渡された引数の総数
$? 特殊変数 直前コマンドの終了ステータス(0: 成功)
"$@" 特殊変数 全引数を個別にクォートして展開
-f ファイル判定 通常ファイルであれば真
-d ファイル判定 ディレクトリであれば真
-z 文字列判定 文字列の長さがゼロ(空文字)であれば真
-eq / -ne 数値比較 等しい / 等しくない
-lt / -gt 数値比較 より小さい(未満) / より大きい(超過)

6.2 コマ問(記述式)頻出キーワード一覧表

以下の設問に対する特殊変数、演算子、または構文キーワードを想起してください。

  • 問題1: Bashスクリプトの第1行目に記述する、インタープリタ指定の先頭2文字(シバン)は何か?
  • 問題2: スクリプトに渡された引数の個数を保持する特殊変数は何か?
  • 問題3: 直前のコマンドが正常終了したかエラーとなったかを表す終了ステータスを保持する特殊変数は何か?
  • 問題4: testコマンドの条件式において、ファイルが「通常ファイルとして存在する」ことを判定するオプションは何か?
  • 問題5: testコマンドの条件式において、変数の値が「空文字列(長さ0)である」ことを判定するオプションは何か?
  • 問題6: if文の終端を示すキーワードは何か?
  • 問題7: case文の終端を示すキーワードは何か?
  • 問題8: for文やwhile文のループ本体を囲む開始・終了キーワードのペアは何か?
解答と解説を見る
設問 正解(記述内容) 補足・別解
問題1 #! 続くパスは /bin/bash
問題2 $# 引数自体は $1, $2
問題3 $? 0が正常、非0がエラー
問題4 -f ディレクトリは -d
問題5 -z 空でない判定は -n
問題6 fi if の逆綴り
問題7 esac case の逆綴り
問題8 dodone do ... done

6.3 Ping-t演習へのブリッジ(対象分野と推奨学習順)

LinuC 102試験における「簡単なスクリプトの作成」(重要度6)は、合否の命運を分ける最重要セクションです。

  • 主題1.06:シェルおよびシェルスクリプト
    • 「簡単なスクリプトのカスタマイズまたは作成」分野(重要度6):Ping-tの問題集を全問「金(正解連続)」ステータスになるまで最優先で反復周回してください。特にコマ問形式での $#$?fiesacdone のスペル入力ミスを完全になくします。

7. まとめと次回予告

  • スクリプトの先頭行には #!/bin/bash を指定し、chmod +x で実行権限を付与します
  • 引数は $1$9${10}、総数は $#、全展開は "$@"、終了コードは $? を用います
  • [ ](testコマンド)の内部には半角スペースが必須であり、ファイルは -f/-d、文字列は -z/-n、数値は -eq/-lt で判定します
  • 構文ブロックの終端キーワード(ifficaseesacfor/whiledo ... done)を正確に記述します
  • 明示的な終了には exit 0(成功)や exit 1(エラー)を用います

次回は主題1.08「ユーザーとグループの管理」より、ユーザーアカウントの作成・変更・削除(useradd / usermod / userdel)と、パスワード管理(/etc/passwd, /etc/shadow, chageコマンド)の技術体系を詳細に解説します。

前の記事
次の記事