「for文とwhile文の使い分けが曖昧なまま、なんとなくコピペして使っている」
こう感じたことがある方は多いはずです。ループ処理はシェルスクリプトの核心部分であり、ここを押さえるだけで自動化できる作業の幅が一気に広がります。
この記事では、bashのfor文・while文・until文の書き方を基礎から解説します。単純な繰り返しから、ファイル一括処理・カウンタループ・break/continueによる制御まで、現場で実際に使えるパターンを中心に紹介します。
この記事のポイント
・for文はリストの各要素を順番に処理する基本ループ
・while文は「条件が真の間」繰り返す。ファイルの行読み込みに最適
・until文は「条件が偽の間」繰り返す。ポーリング処理に便利
・break/continueで途中スキップ・終了ができる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
bashのループ処理の基本を整理する
bashには3種類のループ構文があります。それぞれ得意な場面が異なります。| 構文 | 意味 | 向いている場面 |
|---|---|---|
| for | リストの要素を順番に処理 | ファイル一覧・引数・配列の繰り返し |
| while | 条件が真の間ループ | ファイルの行読み込み・カウンタ・無限ループ |
| until | 条件が偽の間ループ | ポーリング処理・リトライ待機 |
for文の使い方
1. 基本構文とリストループ
for文の基本形は以下のとおりです。# 基本構文 for 変数 in リスト; do 処理 done
#!/bin/bash for color in red green blue; do echo "色: $color" done
色: red 色: green 色: blue
2. コマンド置換でリストを動的生成する
`$(コマンド)` を使うと、コマンドの出力をリストとして使えます。特定ディレクトリ内のファイルを処理する場面でよく使います。#!/bin/bash # /var/log内の.logファイルを列挙して行数カウント for logfile in $(ls /var/log/*.log 2>/dev/null); do count=$(wc -l < "$logfile") echo "$logfile: ${count}行" done
/var/log/messages.log: 4382行 /var/log/secure.log: 127行 /var/log/cron.log: 893行
3. グロブ展開でファイルを直接処理する
`ls` を介さずにグロブ(ワイルドカード)を直接for文に書く書き方が、スペースを含むファイル名にも対応できて実務では推奨されます。#!/bin/bash # /tmpの.csvファイルを処理 for csvfile in /tmp/*.csv; do # ファイルが実在することを確認(マッチなしの場合 /tmp/*.csv がそのまま残る) [ -f "$csvfile" ] || continue echo "処理中: $csvfile" done
4. Cスタイルのカウンタループ
数値のカウンタを使いたい場合は `(( ))` を使ったCスタイルが便利です。#!/bin/bash # 1から10まで出力 for (( i=1; i<=10; i++ )); do echo "$i" done
#!/bin/bash # seqを使った1から10 for i in $(seq 1 10); do echo "$i" done # 2刻みのカウンタ(seq 開始 刻み 終了) for i in $(seq 0 2 20); do echo "$i" done
5. 配列のループ処理
bashの配列を for で回す場合は `"${配列名[@]}"` という形式を使います。#!/bin/bash servers=("web01" "web02" "db01" "db02") for server in "${servers[@]}"; do echo "ping確認: $server" ping -c 1 -W 2 "$server" > /dev/null 2>&1 && echo " OK" || echo " NG" done
while文の使い方
1. 基本構文
while文は「条件が真(終了コード0)の間」ループを続けます。# 基本構文 while 条件; do 処理 done
2. カウンタを使ったwhile文
#!/bin/bash count=1 while [ $count -le 5 ]; do echo "count = $count" count=$(( count + 1 )) done
count = 1 count = 2 count = 3 count = 4 count = 5
3. while read でファイルを1行ずつ読む
`while read` はシェルスクリプトで最もよく使うパターンのひとつです。ファイルの各行をひとつずつ処理します。#!/bin/bash # hostsファイルを1行ずつ読んで処理 while IFS= read -r line; do # コメント行はスキップ [[ "$line" =~ ^# ]] && continue echo "処理: $line" done < /etc/hosts
4. パイプとwhile readの組み合わせ
コマンド出力を1行ずつ処理する際はパイプと組み合わせます。#!/bin/bash # psコマンドの出力からCPU使用率が高いプロセスを抽出 ps aux --sort=-%cpu | head -20 | while IFS= read -r line; do echo "$line" done
#!/bin/bash # プロセス置換でサブシェル問題を回避 count=0 while IFS= read -r line; do (( count++ )) done < <(find /var/log -name "*.log" -type f) echo "ログファイル数: $count"
5. 無限ループと定期実行
監視スクリプトなどで一定間隔で処理を繰り返したい場合は、条件に `true` を使った無限ループが便利です。#!/bin/bash # ディスク使用率を10秒ごとに監視 while true; do usage=$(df /var | awk 'NR==2 {print $5}' | tr -d '%') echo "$(date '+%Y-%m-%d %H:%M:%S') /var 使用率: ${usage}%" if [ "$usage" -ge 80 ]; then echo "警告: /var のディスク使用率が80%を超えました" fi sleep 10 done
until文の使い方
1. 基本構文と特徴
until文は「条件が偽の間」ループを続けます。while文と逆の発想です。# 基本構文 until 条件; do 処理 done
2. サービス起動待ちのポーリング
until文が特に役立つのは、「ある状態になるまで待つ」ポーリング処理です。#!/bin/bash # Apacheが起動するまで待機 MAX_WAIT=60 elapsed=0 until systemctl is-active --quiet httpd; do echo "httpd 待機中... (${elapsed}秒経過)" sleep 5 elapsed=$(( elapsed + 5 )) if [ "$elapsed" -ge "$MAX_WAIT" ]; then echo "エラー: httpd が${MAX_WAIT}秒以内に起動しませんでした" exit 1 fi done echo "httpd が起動しました"
3. リトライ処理
APIや外部コマンドを最大N回リトライする場合もuntilが使いやすいです。#!/bin/bash # curlを最大5回リトライ retry=0 MAX_RETRY=5 until curl -sf https://example.com/api/status; do retry=$(( retry + 1 )) if [ "$retry" -ge "$MAX_RETRY" ]; then echo "エラー: ${MAX_RETRY}回リトライしましたが失敗しました" exit 1 fi echo "リトライ ${retry}/${MAX_RETRY}..." sleep 3 done echo "疎通確認OK"
break・continueでループを制御する
1. break — ループを途中で抜ける
`break` は現在のループを即座に終了します。#!/bin/bash # /var/log のファイルを探して、最初に見つかった1GB超のファイルを報告 for f in /var/log/*; do [ -f "$f" ] || continue size=$(stat -c %s "$f" 2>/dev/null || echo 0) if [ "$size" -ge 1073741824 ]; then echo "1GB超のファイル検出: $f (${size}バイト)" break fi done
2. continue — 現在の反復をスキップする
`continue` は今回のループ処理をスキップして、次の反復に移ります。#!/bin/bash # .log拡張子以外はスキップ for f in /var/log/*; do # ファイルでなければスキップ [ -f "$f" ] || continue # .log以外はスキップ [[ "$f" == *.log ]] || continue echo "処理: $f" done
現場でよく使うループパターン集
1. ファイルを一括リネーム
#!/bin/bash # /tmp の .bak ファイルを .old にリネーム for f in /tmp/*.bak; do [ -f "$f" ] || continue newname="${f%.bak}.old" mv "$f" "$newname" echo "リネーム: $f -> $newname" done
2. 複数サーバーへのSSHコマンド実行
#!/bin/bash # サーバーリストファイルを1行ずつ読み、uptime確認 SERVERLIST=/home/admin/servers.txt while IFS= read -r server; do [[ -z "$server" || "$server" =~ ^# ]] && continue echo "=== $server ===" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 "$server" uptime 2>&1 done < "$SERVERLIST"
3. ログファイルの古いエントリを集計する
#!/bin/bash # /var/log 配下の全.logファイルの行数を集計 total=0 while IFS= read -r logfile; do lines=$(wc -l < "$logfile") total=$(( total + lines )) echo "$logfile: ${lines}行" done < <(find /var/log -name "*.log" -type f 2>/dev/null) echo "---" echo "合計: ${total}行"
ループ処理のトラブルシュート・注意点
1. 無限ループになってしまった時の止め方
スクリプトが無限ループに入った場合は `Ctrl+C` で中断できます。バックグラウンド実行中(`./script.sh &`)の場合は `kill %1` またはプロセスIDを `kill` してください。スクリプトを書くときは最初から `MAX_ITER` 変数でループ上限を設けておくと、テスト中に暴走しても安全です。
2. for文でスペース入りファイル名を扱う
# NG: ls + コマンド置換はスペース入りファイル名で分割される for f in $(ls /tmp); do ... # OK: グロブ展開はスペース入りでも1ファイル=1要素 for f in /tmp/*; do ...
3. set -e と組み合わせる時の注意
スクリプト冒頭に `set -e`(エラー時即終了)を入れている場合、ループ内でコマンドが失敗するとスクリプト全体が終了します。失敗を許容したい行は `コマンド || true` と書いて終了コードを0にしてください。#!/bin/bash set -e for server in web01 web02 web03; do # ping失敗してもスクリプトを続行したい場合 ping -c 1 -W 2 "$server" > /dev/null 2>&1 || true echo "$server の確認完了" done
4. サブシェル変数問題(パイプの罠)
パイプの右側はサブシェルで動くため、変数の更新が外側に伝わりません。# NG: パイプでサブシェルになり count が外側で 0 のまま count=0 cat /etc/hosts | while IFS= read -r line; do (( count++ )) done echo "行数: $count" # 0 になってしまう # OK: プロセス置換でメインシェルで実行 count=0 while IFS= read -r line; do (( count++ )) done < <(cat /etc/hosts) echo "行数: $count" # 正しい行数が出る
本記事のまとめ
| やりたいこと | 書き方 |
|---|---|
| リストをループ | for item in list; do 処理; done |
| ファイル一覧をループ | for f in /path/*.txt; do 処理; done |
| カウンタループ | for (( i=1; i<=10; i++ )); do 処理; done |
| ファイルを1行ずつ読む | while IFS= read -r line; do 処理; done < file |
| 条件が真の間ループ | while [ 条件 ]; do 処理; done |
| 条件が偽の間ループ | until [ 条件 ]; do 処理; done |
| ループを途中で抜ける | break |
| 今回の反復をスキップ | continue |
| サブシェル変数問題を回避 | while ... done < <(コマンド) |
Linuxの基本コマンドと組み合わせた使い方については、Linux基本コマンドの解説も合わせて参照してください。
また、ループの中でファイルやディレクトリを操作する場合に欠かせない ls コマンドの基本オプション と、ファイルをまとめる際によく組み合わせる tar コマンドの実用例 も参考にしてください。
Linux無料マニュアルを受け取る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 次のページへ:gitコマンドの使い方入門|init・clone・commit・pushの基本とLinuxサーバーでの実践例
- 前のページへ:bashのif文とtest条件式の使い方|ファイル・文字列・数値の比較から実践例まで
- この記事の属するカテゴリ:Linuxtips・シェルスクリプトへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます