Bash 入門

Bash 条件分岐

条件分岐はプログラミングロジックの基礎であり、特定の条件が真(true)か偽(false)かに基づいて、スクリプトが異なるコードブロックを実行できるようにします。Bashスクリプトでは、ifthenelseeliffi というキーワードを使用して、これらの条件構造を構築します。

本章では、条件分岐の構文、さまざまな種類の条件、およびBashスクリプト内でそれらを効率的に使用する方法について包括的に解説します。

1. if 文:基本構文

if 文は、Bashにおける最も基本的な条件実行の形式です。特定の条件が真である場合にのみ、特定のコードを実行します。基本構文は以下の通りです。

if [ condition ]; then
  # 条件が真の場合、ここにあるコードを実行
fi
  • if: 条件文を開始するキーワード。
  • [ condition ]: 評価される条件。非常に重要な点として、角括弧内の条件の両側には必ずスペースが必要です。ここでの [ は実際にはコマンドであり、test コマンドのエイリアスです。
  • ;: ステートメントの区切り記号。then と条件を同一行に記述する場合、セミコロンが必要です。
  • then: 条件が真の時に実行するコードブロックの開始を示すキーワード。
  • # コード...: 実行されるコード。1つ以上のBashコマンドを含めることができます。
  • fi: if 文の終了を示すキーワード(if を逆から綴ったもの)。

例:ファイルの存在確認

#!/bin/bash
file="my_file.txt"

if [ -f "$file" ]; then
  echo "ファイル '$file' は存在します。"
fi

この例では:

  • -f "$file" が条件です。-f オプションは、変数 $file で指定されたファイルが存在し、かつそれが通常のファイル(regular file)であるかどうかをテストします。
  • ファイルが存在する場合、コンソールに "ファイル 'my_file.txt' は存在します。" というメッセージが表示されます。

2. else 文

if 文の条件が偽(false)であった場合に、else 文は代替のコードブロックを実行します。構文は以下の通りです。

if [ condition ]; then
  # 条件が真の場合、ここを実行
else
  # 条件が偽の場合、ここを実行
fi

例:ディレクトリの存在を確認し、なければ作成する

#!/bin/bash
dir="my_directory"

if [ -d "$dir" ]; then
  echo "ディレクトリ '$dir' は既に存在します。"
else
  echo "ディレクトリ '$dir' は存在しません。新しく作成します..."
  mkdir "$dir"
fi

この例では:

  • -d "$dir" が条件です。-d オプションは、変数 $dir で指定されたディレクトリが存在するかどうかをテストします。
  • ディレクトリが存在すればメッセージを表示し、存在しなければ別のメッセージを表示して mkdir コマンドを実行し、ディレクトリを作成します。

3. elif 文

elif(else if の略)文を使用すると、複数の条件を連鎖させることができます。前の if または elif の条件が偽であった場合に、追加の条件をテストする方法を提供します。構文は以下の通りです。

if [ condition1 ]; then
  # condition1 が真の場合、ここを実行
elif [ condition2 ]; then
  # condition1 が偽で、かつ condition2 が真の場合、ここを実行
else
  # condition1 と condition2 が両方とも偽の場合、ここを実行
fi

複数の elif 文を記述して、一連の条件をテストすることが可能です。

例:数値の範囲に基づいて評価を行う

#!/bin/bash
num=75

if [ "$num" -gt 90 ]; then
  echo "優秀"
elif [ "$num" -gt 70 ]; then
  echo "良好"
elif [ "$num" -gt 50 ]; then
  echo "合格"
else
  echo "改善の余地あり"
fi

この例では:

  • -gt は数値比較演算子(Greater Than / ~より大きい)です。
  • スクリプトは $num の値がどの範囲に該当するかを順番にチェックし、対応するメッセージを表示します。

4. 条件の組み合わせ

Bashでは、論理演算子を使用して複数の条件を組み合わせることができます。

  • -a または &&: 論理積(AND)。すべての条件が真である必要があります。
  • -o または ||: 論理和(OR)。少なくとも1つの条件が真である必要があります。
  • !: 論理否定(NOT)。条件の真偽を反転させます。

例:ファイルが存在し、かつ読み取り権限があるかを確認

#!/bin/bash
file="my_file.txt"

if [ -f "$file" -a -r "$file" ]; then
  echo "ファイル '$file' は存在し、読み取り可能です。"
else
  echo "ファイル '$file' は存在しないか、あるいは読み取り不可能です。"
fi

この例では:

  • -r "$file" はファイルが読み取り可能かをテストします。
  • -a 演算子により、ファイルが存在し、かつ読み取り可能である場合のみ then ブロックが実行されます。

同じ条件を && を使って記述することもできます。

if [ -f "$file" ] && [ -r "$file" ]; then
  echo "ファイル '$file' は存在し、読み取り可能です。"
else
  echo "ファイル '$file' は存在しないか、あるいは読み取り不可能です。"
fi

例:変数が空か、または特定の値を含んでいるかを確認

#!/bin/bash
variable=""

if [ -z "$variable" ] || [ "$variable" = "default" ]; then
  echo "変数は空であるか、または値が 'default' です。"
fi

この例では:

  • -z "$variable" は変数が空(長さがゼロ)であるかをテストします。
  • "$variable" = "default" は変数が文字列 "default" と等しいかをテストします。= の両側にはスペースが必要です。
  • || 演算子により、どちらかの条件が真であれば then ブロックが実行されます。

5. テストコマンドの代替案:[[ ]]

Bashは、二重角括弧 [[ ]] を使用した条件式の代替構文を提供しています。一重の角括弧 [ ] と比較して、いくつかの利点があります。

  • 特定の特殊文字をエスケープする必要がない。
  • =~ を使用した正規表現によるパターンマッチングをサポート。
  • ワードスプリッティング(Word splitting)やパス名展開が行われない。

例:[[ ]] と正規表現の組み合わせ

#!/bin/bash
string="hello world"

if [[ "$string" =~ "world" ]]; then
  echo "文字列に 'world' が含まれています。"
fi

この例では:

  • =~[[ ]] 内部で使用される正規表現マッチング演算子です。
  • 変数 $string に部分文字列 "world" が含まれているかをチェックします。=~ の右側は拡張正規表現として解析されます。

例:[[ ]] で引用符の問題を回避する

#!/bin/bash
file="my file.txt"

# ファイルが存在しない場合でも、二重括弧ならエラーになりにくい
if [[ -f "$file" ]]; then
  echo "ファイルは存在します"
fi

# 上記のコードは test ([]) を使用した以下のコードと同等です
if test -f "$file"; then
  echo "ファイルは存在します"
fi

[[ ]] を使用する場合、通常はスペースを含む変数の引用符(クォーティング)を心配する必要はありませんが、コードの明確化のために引用符を付ける習慣は推奨されます。一方、test コマンド(つまり [)を使用する場合は、不測の挙動を防ぐために引用符は必須です。

6. 数値比較

Bashで数値を比較する場合、[ ] または [[ ]] 内で以下の演算子を使用します。

  • -eq: 等しい (Equal to)
  • -ne: 等しくない (Not equal to)
  • -gt: より大きい (Greater than)
  • -ge: 以上 (Greater than or equal to)
  • -lt: より小さい (Less than)
  • -le: 以下 (Less than or equal to)

例:2つの数字を比較する

#!/bin/bash
num1=10
num2=20

if [ "$num1" -lt "$num2" ]; then
  echo "$num1 は $num2 より小さいです。"
fi

7. 文字列比較

文字列を比較する場合、[ ] または [[ ]] 内で以下の演算子を使用します。

  • =: 等しい
  • ==: 等しい(= と同じ。[[ ]] 内で一般的)
  • !=: 等しくない
  • -z: 文字列が空(長さがゼロ)であれば真
  • -n: 文字列が空でなければ真

例:2つの文字列を比較する

#!/bin/bash
str1="hello"
str2="world"

if [ "$str1" != "$str2" ]; then
  echo "$str1 は $str2 と等しくありません。"
fi

重要: [ ] 内で =!= を使用する際は、必ず変数を引用符で囲んでください。これにより、変数にスペースが含まれている場合の不具合を回避できます。

8. ファイルの存在確認とタイプチェック

Bashは、ファイルの存在やタイプをチェックするための多くの演算子を提供しています。

  • -e: ファイルが存在すれば真 (exists)
  • -f: ファイルが存在し、かつ通常のファイルであれば真 (file)
  • -d: ファイルが存在し、かつディレクトリであれば真 (directory)
  • -r: ファイルが存在し、かつ読み取り可能であれば真 (readable)
  • -w: ファイルが存在し、かつ書き込み可能であれば真 (writable)
  • -x: ファイルが存在し、かつ実行可能であれば真 (executable)
  • -s: ファイルが存在し、かつサイズがゼロより大きければ真 (size)

例:ファイルが実行可能かを確認する

#!/bin/bash
file="my_script.sh"

if [ -x "$file" ]; then
  echo "ファイル '$file' は実行可能です。"
else
  echo "ファイル '$file' は実行不可能です。"
fi

9. 終了ステータスコード

Bashでは、すべてのコマンドが終了ステータスコード(Exit Status)を返します。これはコマンドが成功したかどうかを示す整数値です。0 は成功、0以外は失敗を意味します。直前に実行したコマンドの終了ステータスは、$? 変数を使用して取得できます。

例:mkdir の終了ステータスを確認する

#!/bin/bash
mkdir "new_directory"

if [ "$?" -eq 0 ]; then
  echo "ディレクトリの作成に成功しました。"
else
  echo "ディレクトリの作成に失敗しました。"
fi

if 条件の中で直接コマンドを実行する、より簡潔な書き方もあります。

#!/bin/bash
if mkdir "new_directory"; then
  echo "ディレクトリの作成に成功しました。"
else
  echo "ディレクトリの作成に失敗しました。"
fi

この例では、if 文が直接 mkdir コマンドを実行します。mkdir が成功(終了コード 0 を返却)すれば、then ブロックが実行されます。失敗すれば else ブロックが実行されます。これはBashスクリプトにおけるエラーチェックの非常に効率的かつ一般的な手法です。

10. 実戦:システム管理スクリプトへのエラーチェックの実装

これまでのモジュールで学んだシステム管理スクリプトに、条件分岐を利用してエラーチェック機能を追加してみましょう。ここでは、新しいユーザーアカウントを作成するスクリプトを想定します。

#!/bin/bash
username="newuser"

# ユーザーが既に存在するか確認
if id "$username" &>/dev/null; then
  echo "エラー:ユーザー '$username' は既に存在します。"
  exit 1  # エラーコードを返してスクリプトを終了
fi

# ユーザー作成
useradd "$username"

# ユーザー作成が成功したか確認
if [ "$?" -ne 0 ]; then
  echo "エラー:ユーザー '$username' の作成に失敗しました。"
  exit 1
fi

echo "ユーザー '$username' を正常に作成しました。"

# パスワード設定(実際の運用では、より安全な方法を使用してください)
echo "$username:password" | chpasswd

# パスワード設定が成功したか確認
if [ "$?" -ne 0 ]; then
  echo "警告:ユーザー '$username' のパスワード設定に失敗しました。"
  # ここでは終了せず警告のみとする。ユーザー作成自体は成功しているため。
fi

echo "ユーザー '$username' のパスワード設定が完了しました。"
exit 0 # 成功ステータスで終了

10.1 改善ポイントの解析

  • id "$username" &>/dev/null: ユーザー情報を取得しようと試みます。&>/dev/null は標準出力 (stdout) と標準エラー (stderr) の両方を /dev/null に捨て、出力を無効化します。ユーザーが存在すれば 0(成功)、存在しなければ 0以外(失敗)を返します。
  • exit 1: エラーコード 1 でスクリプトを終了します。適切なエラーコードを返すことは、そのスクリプトを呼び出す他のプログラムやシステムが失敗を検知するために重要です。
  • パスワード設定の警告: パスワード設定に失敗した場合、警告を表示しますが終了はしません。ユーザー作成自体は成功しているため、致命的なエラーとはみなさない判断です。
  • exit 0: エラーが発生しなかった場合、最後に成功コード (0) で終了します。

この例は、条件分岐と終了ステータスコードを運用することで、Bashスクリプトに堅牢なエラーチェックを追加し、信頼性とデバッグのしやすさを向上させる方法を示しています。