bashのfor文・while文・until文の使い方|ループ処理の基本から実践例まで

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Linuxtips, シェルスクリプト > bashのfor文・while文・until文の使い方|ループ処理の基本から実践例まで
「シェルスクリプトでファイルを一括処理したいが、ループの書き方がよくわからない」
「for文とwhile文の使い分けが曖昧なまま、なんとなくコピペして使っている」

こう感じたことがある方は多いはずです。ループ処理はシェルスクリプトの核心部分であり、ここを押さえるだけで自動化できる作業の幅が一気に広がります。

この記事では、bashのfor文・while文・until文の書き方を基礎から解説します。単純な繰り返しから、ファイル一括処理・カウンタループ・break/continueによる制御まで、現場で実際に使えるパターンを中心に紹介します。

この記事のポイント

・for文はリストの各要素を順番に処理する基本ループ
・while文は「条件が真の間」繰り返す。ファイルの行読み込みに最適
・until文は「条件が偽の間」繰り返す。ポーリング処理に便利
・break/continueで途中スキップ・終了ができる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
図解60P/登録10秒/解除も3秒 / 詳細はこちら

bashのループ処理の基本を整理する

bashには3種類のループ構文があります。それぞれ得意な場面が異なります。

構文 意味 向いている場面
for リストの要素を順番に処理 ファイル一覧・引数・配列の繰り返し
while 条件が真の間ループ ファイルの行読み込み・カウンタ・無限ループ
until 条件が偽の間ループ ポーリング処理・リトライ待機
「どれを選ぶか迷う」という場合は、繰り返す対象がリスト(ファイル・配列・コマンド出力)なら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

`in` の後に書いたスペース区切りのリストが、ループの1回ごとに変数へ代入されます。

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行

ただし、ファイル名にスペースが含まれると誤動作します。その場合は後述の `while read` を使うほうが安全です。

3. グロブ展開でファイルを直接処理する

`ls` を介さずにグロブ(ワイルドカード)を直接for文に書く書き方が、スペースを含むファイル名にも対応できて実務では推奨されます。

#!/bin/bash # /tmpの.csvファイルを処理 for csvfile in /tmp/*.csv; do # ファイルが実在することを確認(マッチなしの場合 /tmp/*.csv がそのまま残る) [ -f "$csvfile" ] || continue echo "処理中: $csvfile" done

グロブ展開はシェルが行うため、ファイル名にスペースが含まれていても1ファイル=1ループ回となります。より安全なのでこちらを習慣にしましょう。

4. Cスタイルのカウンタループ

数値のカウンタを使いたい場合は `(( ))` を使ったCスタイルが便利です。

#!/bin/bash # 1から10まで出力 for (( i=1; i<=10; i++ )); do echo "$i" done

または `seq` コマンドと組み合わせる方法もよく使われます。

#!/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

サーバー名やIPアドレスを配列にまとめて一括ping確認するのは、現場でよく見かけるパターンです。

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

`IFS=` でフィールド区切り文字を空にすることで、行頭・行末のスペースを保持します。`-r` はバックスラッシュのエスケープを無効にするオプションで、パスを正確に読み込むために必要です。

4. パイプとwhile readの組み合わせ

コマンド出力を1行ずつ処理する際はパイプと組み合わせます。

#!/bin/bash # psコマンドの出力からCPU使用率が高いプロセスを抽出 ps aux --sort=-%cpu | head -20 | while IFS= read -r line; do echo "$line" done

ただし、パイプの右側でwhile readを使うと「サブシェル内」で実行されるため、変数の変更がループ外に伝わらないという落とし穴があります。変数を外側で使いたい場合はプロセス置換(`< <(コマンド)`)を使います。

#!/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

本番では `systemd` のタイマーやcronで定期実行するほうが堅牢ですが、作業中の一時的な監視にはこのパターンが重宝します。

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 が起動しました"

デプロイスクリプトでサービスの起動確認を待つ場面で、until文はwhileより意図が読みやすいコードになります。

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

`break 2` のように数値を付けると、ネストしたループを外側まで一気に抜けられます。

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

`${変数%.bak}` はパラメータ展開で末尾の `.bak` を除去する記法です。シェルスクリプトのリネーム処理で必須の書き方です。

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"

このパターンはサーバー台数が多い場合の一括チェックで実際によく使います。サーバーリストをテキストファイルで管理して `while read` で回す構成は、メンテナンスしやすくて現場受けがいいです。

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 < <(コマンド)
ループ処理を身につけると、「手作業で10分かかる作業を10秒で終わらせる」スクリプトが書けるようになります。まずはfor文のグロブ展開と `while IFS= read -r` の2パターンを習慣化するだけで、日常の作業効率が大きく変わります。

Linuxの基本コマンドと組み合わせた使い方については、Linux基本コマンドの解説も合わせて参照してください。

また、ループの中でファイルやディレクトリを操作する場合に欠かせない ls コマンドの基本オプション と、ファイルをまとめる際によく組み合わせる tar コマンドの実用例 も参考にしてください。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Linux無料マニュアルを受け取る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

登録10秒/合わなければ解除3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。