Bash 入門

Bash のbreakとcontinue文

break および continue 文は、Bashスクリプト内のループプロセスの制御において非常に強力なメカニズムを提供します。これらを使用することで、特定の条件に基づいてループの通常の実行軌道を変更し、スクリプトをより効率的かつ柔軟に構築できます。

1. break と continue の理解

breakcontinue はどちらも制御フロー文であり、特にループ構造(forwhileuntil など)の内部で使用され、コードの実行方法を修正するために用いられます。

1.1 break 文

break 文の役割は、現在のループを終了し、プログラムの制御をループ本体の直後にある次の文に移すことです。これをループの「緊急出口」と考えることができます。コードの実行が break に達すると、ループ自体の条件が依然として満たされていても、ループは即座に停止します。

コード例:

#!/bin/bash

for i in {1..10}
do
  if [ $i -gt 5 ]; then
    echo "i = $i の時にループを中断 (Breaking the loop)"
    break  # ループを抜ける
  fi
  echo "i = $i"
done

echo "ループ終了。"

この例では、ループは本来 1 から 10 までイテレートする予定でした。しかし、i の値が 5 より大きくなった時に break 文が実行され、ループは途中で終了します。

出力結果:

i = 1
i = 2
i = 3
i = 4
i = 5
i = 6 の時にループを中断 (Breaking the loop)
ループ終了。

原理の解析: スクリプトは 1 から 10 までの数値を走査します。i が 6 になった時、条件 $i -gt 5 が真(true)と判定されます。その後、break 文がトリガーされ、ループが即座に終了します。続いて、スクリプトループ本体の外部にある次の行を実行し、「ループ終了。」と出力します。

1.2 continue 文

continue 文の役割は、現在のループイテレーションの残りの部分をスキップし、直接次のイテレーションに進むことです。これは現在のサイクル内の continue 以降のコマンドを実行せずに、ループの次の周期に直接ジャンプすることを意味します。

コード例:

#!/bin/bash

for i in {1..10}
do
  if [ $((i % 2)) -eq 0 ]; then
    continue  # 偶数をスキップ
  fi
  echo "i = $i"
done

echo "ループ終了。"

この例では、ループは 1 から 10 までイテレートします。i が偶数の時、continue 文が実行され、そのイテレーション内の echo コマンドがスキップされます。

出力結果:

i = 1
i = 3
i = 5
i = 7
i = 9
ループ終了。

原理の解析: スクリプトは 1 から 10 までの数値を走査します。各偶数に対して、条件 $((i % 2)) -eq 0 が真になります。continue 文が実行されると、現在のイテレーションの残りの部分(すなわち echo コマンド)がスキップされます。その後、スクリプトは直接ループの次の数値へと進み、イテレーションを継続します。

2. ネストされたループとラベル

デフォルトでは、breakcontinue はそれらが記述されている最も内側のループにのみ影響します。内側のループから外側のループの動作を制御したい場合は、ラベル(labels)を使用する必要があります。

2.1 ラベル付きの break

コード例:

#!/bin/bash

outer_loop: for i in {1..3}
do
  echo "外側のループ i = $i"
  for j in {1..3}
  do
    echo "  内側のループ j = $j"
    if [ $i -eq 2 ] && [ $j -eq 2 ]; then
      echo "  外側のループを中断!"
      break outer_loop # outer_loop ラベルが付いた外側のループを直接抜ける
    fi
  done
done

echo "ループ終了。"

出力結果:

外側のループ i = 1
  内側のループ j = 1
  内側のループ j = 2
  内側のループ j = 3
外側のループ i = 2
  内側のループ j = 1
  内側のループ j = 2
  外側のループを中断!
ループ終了。

原理の解析: この例は、内側のループ内の特定の条件が満たされた時に、どのようにして特定の(外側の)ループを直接抜けるかを示しています。outer_loop: ラベルは外側のループを識別します。その後の break outer_loop コマンドは、正確にそのマークされたループの階層から脱出します。

2.2 ラベル付きの continue

(注意:この使い方は比較的稀ですが、完全に有効です)

コード例:

#!/bin/bash

outer_loop: for i in {1..5}
do
  for j in {1..3}
  do
    if [ $j -eq 2 ]; then
      continue outer_loop # 外側のループの残りの部分をスキップし、外側のループの次のイテレーションへ進む
    fi
    echo "i=$i, j=$j"
  done
  echo "外側のループ i=$i のイテレーション終了"
done

出力結果:

i=1, j=1
i=3, j=1
i=4, j=1
i=5, j=1

原理の解析: continue outer_loop 文は現在の外側ループイテレーションの残りの内容をスキップし、直接次の i 値へとジャンプします。そのため、「外側のループイテレーション終了」というメッセージや j=3 の時のメッセージはすべてスキップされます。なぜなら外側のループが即座に再起動(次のサイクルへ移行)されたからです。

3. 実践的なユースケースのデモンストレーション

3.1 ディレクトリ内の特定ファイルをスキップする

多様なファイルが含まれるディレクトリがあり、.txt ファイルのみを処理し、すべての .log ログファイルをスキップしたいと仮定します。

#!/bin/bash

for file in *
do
  if [[ "$file" == *.log ]]; then
    echo "ログファイルをスキップ: $file"
    continue # .log ファイルをスキップ
  fi
  echo "ファイルを処理中: $file"
  # ここにファイル処理のロジックを追加
done

この例では、ループがカレントディレクトリ下のすべてのファイルを走査します。ファイル名が .log で終わる場合、continue 文がループ本体の残りの部分をスキップし、直接次のファイルを確認しに行きます。

3.2 特定のエラー発生時にループを終了する

ファイルを 1 行ずつ読み込み、無効なフォーマットの行に遭遇した時に処理を停止したいと仮定します。

#!/bin/bash

while IFS= read -r line
do
  if [[ ! "$line" =~ ^[0-9]+,[A-Za-z]+$ ]]; then
    echo "無効なフォーマットに遭遇: $line"
    break # フォーマットが無効な場合、ループを終了
  fi
  # 行データを処理(フォーマットが正しいことが前提)
  echo "行を処理中: $line"
done < data.txt

ここでは、ループdata.txt から各行を読み取ります。ある行のデータが期待されるフォーマット(例:数値、コンマ、アルファベット)に適合しない場合、break 文がループ全体を終了させます。

もし data.txt に以下の内容が含まれている場合:

123,Valid
456,AnotherValid
Invalid Data
789,StillValid

出力結果は次のようになります:

行を処理中: 123,Valid
行を処理中: 456,AnotherValid
無効なフォーマットに遭遇: Invalid Data

3.3 break を使用したシンプルなメニューの実装

この例では、メニュー画面を表す無限の while ループを break で終了させます。

#!/bin/bash

while true; do
  echo "メニュー:"
  echo "1. オプション 1"
  echo "2. オプション 2"
  echo "3. 終了"
  read -p "選択肢を入力してください: " choice

  case $choice in
    1)
      echo "オプション 1 を実行中"
      ;;
    2)
      echo "オプション 2 を実行中"
      ;;
    3)
      echo "終了中..."
      break # ループを抜ける
      ;;
    *)
      echo "無効な選択です。再試行してください。"
      ;;
  esac
done

echo "プログラム実行終了。"

この例では、while true ループはユーザーが '3' を入力するまで無限に実行されます。'3' が入力されると break 文がトリガーされ、ループが終了します。

3.4 continue を使用した特定範囲の数値フィルタリング

1 から 20 までの数値を出力しますが、3 の倍数をスキップするデモンストレーションです。

#!/bin/bash

for i in $(seq 1 20); do
  if [[ $((i % 3)) -eq 0 ]]; then
    continue # 3 の倍数をスキップ
  fi
  echo "$i"
done

このスクリプトは 1 から 20 までの数値を出力しますが、3, 6, 9, 12, 15, 18 は除外されます。