for
| 対応: | for 変数 in リスト | POSIX(sh互換) |
|---|---|---|
| C言語風 for (( )) | Bash(bash拡張) |
『シェルスクリプト』の for 文は、リストやシーケンスの各要素に対して同じ処理を繰り返す構文です。for 変数 in リスト 形式で要素を1つずつ取り出す書き方が基本で、bash ではさらに C 言語風の for ((i=0; i<n; i++)) 形式も使えます。コマンド置換($(コマンド))と組み合わせればコマンドの出力をそのままリストとして扱え、ファイルグロブと組み合わせればディレクトリ内のファイルを一括処理できます。
基本構文
for ループは do でブロックを開始し、done で閉じます。in の後に列挙した値・変数・コマンド置換の結果が1つずつ変数に代入されて繰り返されます。
| キーワード | 意味 | 説明 |
|---|---|---|
for | ループの開始 | ループ変数とリストを宣言します。 |
in | リストの指定 | 反復対象となる値の一覧を記述します。省略すると $@(引数全体)を使います。 |
do | 処理ブロックの開始 | 繰り返す処理をここから書き始めます。 |
done | 処理ブロックの終了 | ループを閉じます(do を逆から読んだ語です)。 |
break | ループの中断 | 現在の for ループを即座に抜けます。 |
continue | 次の反復へスキップ | 以降の処理をスキップして次の要素の処理に進みます。 |
# for 文の基本的な書き方(for...in 形式)
for 変数 in 値1 値2 値3; do
# 変数 に 値1 → 値2 → 値3 の順に代入されて実行されます
処理
done
# C 言語風の数値カウント(bash 専用)
for ((i=0; i<n; i++)); do
処理
done
# ブレース展開でシーケンスを作る(bash 専用)
for i in {1..5}; do
処理
done
# コマンド置換でコマンドの出力を反復します
for 変数 in $(コマンド); do
処理
done
# グロブでファイルを反復します
for file in /path/to/*.txt; do
処理
done
for...in リスト形式
in の後に空白区切りで値を並べるか、配列を展開して使います。配列全体を展開するときは "${配列名[@]}" と記述してください。ダブルクォートで囲まないと、スペースを含む要素が単語分割されてしまいます。
| 書き方 | 説明 |
|---|---|
for x in a b c | 値を直接列挙します。スペース区切りでリストを指定します。 |
for x in "${arr[@]}" | 配列の全要素を安全に展開します。スペースを含む要素も1つとして扱われます。 |
for x in "${arr[*]}" | 配列を1つの文字列として展開します。IFS の区切り文字でつながれるため通常は [@] を使います。 |
for x in {1..10} | 1 から 10 までの整数シーケンスを展開します(bash 専用)。 |
for x in {0..9..2} | 0 から 9 まで 2 ステップずつ展開します(bash 専用、ステップ指定)。 |
C 言語風 for 形式(bash 専用)
for ((初期化; 条件; 更新)) 形式は bash 専用の算術 for ループです。インデックスを使った繰り返しや、カウンタを制御したい場合に便利です。sh では使えないため、移植性が必要なスクリプトでは while ループで代替してください。
| 部分 | 説明 |
|---|---|
| 初期化 | ループ開始前に1度だけ実行されます。カウンタ変数の初期値を設定します。 |
| 条件 | 各反復の前に評価されます。0(真)の間ループを続けます。 |
| 更新 | 各反復の後に実行されます。カウンタのインクリメントなどを記述します。 |
# C 言語風の基本的な書き方
for ((i=0; i<5; i++)); do
echo "i = ${i}"
done
# デクリメント(カウントダウン)
for ((i=5; i>0; i--)); do
echo "カウントダウン: ${i}"
done
# ステップを 2 ずつ増やす
for ((i=0; i<=10; i+=2)); do
echo "偶数: ${i}"
done
コマンド置換との組み合わせ
$(コマンド) の出力を for のリストとして使えます。seq コマンドや ls・find・cat の出力をそのまま反復できるため、外部コマンドと組み合わせた柔軟な処理が書けます。ただし、スペースや改行を含むファイル名を扱う場合はグロブ展開のほうが安全です。
| よく使う組み合わせ | 説明 |
|---|---|
for i in $(seq 1 10) | seq コマンドで 1〜10 の数値リストを生成します。sh でも動作します。 |
for line in $(cat file.txt) | ファイルの各行を反復します。スペースを含む行は分割されるため注意が必要です。 |
for f in $(find . -name "*.log") | find の結果を反復します。スペースを含むパスには対応できません。 |
for host in $(cat hosts.txt) | ホストリストファイルの各行を反復します。SSH 一括処理などに使います。 |
ファイル一覧の反復処理
ディレクトリ内のファイルを処理するには、コマンド置換よりグロブ展開(*.txt など)のほうが安全です。グロブはシェルが展開するため、スペースを含むファイル名にも対応できます。ファイルが1件もない場合の対策として nullglob オプション(bash)を使う方法もあります。
| 書き方 | 説明 |
|---|---|
for f in /dir/*.txt | 指定ディレクトリの .txt ファイルを反復します。 |
for f in ./* | カレントディレクトリの全ファイル・ディレクトリを反復します。 |
for f in /dir/**/*.log | 再帰的にサブディレクトリを含めて反復します(bash の globstar オプションが必要です)。 |
shopt -s nullglob | マッチするファイルが0件のときグロブをそのまま文字列として扱わず展開しないようにします。 |
サンプルコード
yakuza_for_in.sh
#!/bin/bash
# -----------------------------------------------
# 龍が如くシリーズのキャラクターで
# for...in リスト形式の使い方を確認します
# -----------------------------------------------
# -----------------------------------------------
# キャラクター名を配列に格納します
# -----------------------------------------------
characters=("桐生一馬" "真島吾朗" "秋山駿" "冴島大河" "錦山彰")
echo "=== 龍が如く キャラクター一覧 ==="
echo ""
# for...in で配列の全要素を反復します
# "${配列[@]}" と必ずダブルクォートで囲んでください
# スペースを含む名前でも1要素として正しく扱われます
for chara in "${characters[@]}"; do
echo " - ${chara}"
done
echo ""
echo "=== 番号付きで表示します ==="
echo ""
# インデックス付きで表示するには C 言語風 for を使います
for ((i=0; i<${#characters[@]}; i++)); do
# ${#配列[@]} で配列の要素数を取得します
echo " ${i}: ${characters[$i]}"
done
echo ""
echo "キャラクター数: ${#characters[@]} 人"
$ chmod +x yakuza_for_in.sh $ ./yakuza_for_in.sh === 龍が如く キャラクター一覧 === - 桐生一馬 - 真島吾朗 - 秋山駿 - 冴島大河 - 錦山彰 === 番号付きで表示します === 0: 桐生一馬 1: 真島吾朗 2: 秋山駿 3: 冴島大河 4: 錦山彰 キャラクター数: 5 人
yakuza_c_style_for.sh
#!/bin/bash
# -----------------------------------------------
# 龍が如くシリーズのキャラクターで
# C 言語風 for ループの使い方を確認します
# -----------------------------------------------
# -----------------------------------------------
# キャラクターの戦闘力スコアを配列に格納します
# -----------------------------------------------
names=("桐生一馬" "真島吾朗" "秋山駿" "冴島大河" "錦山彰")
scores=(9800 9500 8200 9100 8700)
echo "=== 戦闘力ランキング(C 言語風 for) ==="
echo ""
# C 言語風 for でインデックスを制御します
# i=0 から配列の要素数未満まで 1 ずつ増やします
for ((i=0; i<${#names[@]}; i++)); do
name="${names[$i]}"
score="${scores[$i]}"
printf " %d位 %-16s スコア: %d\n" "$((i + 1))" "${name}" "${score}"
done
echo ""
echo "=== カウントダウン(デクリメント) ==="
echo ""
# i を 5 から 1 まで 1 ずつ減らします
for ((i=${#names[@]}; i>=1; i--)); do
echo " 第${i}位: ${names[$((i - 1))]}"
done
echo ""
echo "=== 偶数インデックスのキャラクターだけ表示します ==="
echo ""
# ステップを 2 ずつ増やして偶数インデックスのみ取り出します
for ((i=0; i<${#names[@]}; i+=2)); do
echo " ${names[$i]}(インデックス ${i})"
done
$ chmod +x yakuza_c_style_for.sh $ ./yakuza_c_style_for.sh === 戦闘力ランキング(C 言語風 for) === 1位 桐生一馬 スコア: 9800 2位 真島吾朗 スコア: 9500 3位 冴島大河 スコア: 9100 4位 錦山彰 スコア: 8700 5位 秋山駿 スコア: 8200 === カウントダウン(デクリメント) === 第5位: 錦山彰 第4位: 冴島大河 第3位: 秋山駿 第2位: 真島吾朗 第1位: 桐生一馬 === 偶数インデックスのキャラクターだけ表示します === 桐生一馬(インデックス 0) 秋山駿(インデックス 2) 錦山彰(インデックス 4)
yakuza_cmd_sub_for.sh
#!/bin/bash
# -----------------------------------------------
# コマンド置換と for ループを組み合わせて
# キャラクターデータファイルを処理します
# -----------------------------------------------
# -----------------------------------------------
# 作業ディレクトリとデータファイルを準備します
# -----------------------------------------------
work_dir="./yakuza_data"
mkdir -p "${work_dir}"
# 各キャラクターの情報ファイルを作成します
echo "桐生一馬 神室町 極道" > "${work_dir}/kiryu.txt"
echo "真島吾朗 蒼天堀 狂犬" > "${work_dir}/majima.txt"
echo "秋山駿 横浜 探偵" > "${work_dir}/akiyama.txt"
echo "冴島大河 北海道 熊殺し" > "${work_dir}/saejima.txt"
echo "錦山彰 神室町 龍" > "${work_dir}/nishikiyama.txt"
echo "=== seq コマンドと組み合わせた繰り返し ==="
echo ""
# seq コマンドで 1〜5 の数値を生成して反復します
# $() コマンド置換で seq の出力をリストとして使います
for i in $(seq 1 5); do
echo " ラウンド ${i} 開始"
done
echo ""
echo "=== find の結果を反復してファイルを処理します ==="
echo ""
# find でテキストファイルを検索してコマンド置換で受け取ります
# スペースを含まないファイル名であれば安全に使えます
for filepath in $(find "${work_dir}" -name "*.txt" | sort); do
filename=$(basename "${filepath}" .txt)
content=$(cat "${filepath}")
echo " [${filename}] ${content}"
done
echo ""
echo "=== グロブ展開でファイルを安全に反復します ==="
echo ""
# グロブ展開はスペースを含むファイル名にも対応できます
# コマンド置換より安全なため、通常はグロブを推奨します
for f in "${work_dir}"/*.txt; do
# -f でファイルの存在を確認してから処理します
if [ -f "${f}" ]; then
echo " 処理中: $(basename ${f})"
fi
done
# 作業ディレクトリを削除してクリーンアップします
rm -rf "${work_dir}"
echo ""
echo "クリーンアップ完了。"
$ chmod +x yakuza_cmd_sub_for.sh $ ./yakuza_cmd_sub_for.sh === seq コマンドと組み合わせた繰り返し === ラウンド 1 開始 ラウンド 2 開始 ラウンド 3 開始 ラウンド 4 開始 ラウンド 5 開始 === find の結果を反復してファイルを処理します === [akiyama] 秋山駿 横浜 探偵 [kiryu] 桐生一馬 神室町 極道 [majima] 真島吾朗 蒼天堀 狂犬 [nishikiyama] 錦山彰 神室町 龍 [saejima] 冴島大河 北海道 熊殺し === グロブ展開でファイルを安全に反復します === 処理中: akiyama.txt 処理中: kiryu.txt 処理中: majima.txt 処理中: nishikiyama.txt 処理中: saejima.txt クリーンアップ完了。
yakuza_break_continue.sh
#!/bin/bash
# -----------------------------------------------
# break と continue の使い方を確認します
# -----------------------------------------------
names=("桐生一馬" "真島吾朗" "秋山駿" "冴島大河" "錦山彰")
# 各キャラクターの状態フラグです(1: 出撃可能, 0: 休養中)
status=(1 1 0 1 0)
echo "=== continue: 休養中のキャラクターをスキップします ==="
echo ""
for ((i=0; i<${#names[@]}; i++)); do
# status が 0(休養中)のときは continue でスキップします
if [ "${status[$i]}" -eq 0 ]; then
echo " ${names[$i]}: 休養中のためスキップします"
continue
fi
echo " ${names[$i]}: 出撃準備完了"
done
echo ""
echo "=== break: 桐生一馬が見つかったら探索を中断します ==="
echo ""
target="桐生一馬"
found=0
for chara in "${names[@]}"; do
echo " 探索中: ${chara}"
# target と一致したら break でループを抜けます
if [ "${chara}" = "${target}" ]; then
echo " → ${target} を発見しました!探索を終了します。"
found=1
break
fi
done
echo ""
if [ "${found}" -eq 1 ]; then
echo "発見: ${target}"
else
echo "${target} は見つかりませんでした。"
fi
$ chmod +x yakuza_break_continue.sh $ ./yakuza_break_continue.sh === continue: 休養中のキャラクターをスキップします === 桐生一馬: 出撃準備完了 真島吾朗: 出撃準備完了 秋山駿: 休養中のためスキップします 冴島大河: 出撃準備完了 錦山彰: 休養中のためスキップします === break: 桐生一馬が見つかったら探索を中断します === 探索中: 桐生一馬 → 桐生一馬 を発見しました!探索を終了します。 発見: 桐生一馬
概要
『シェルスクリプト』の for 文には for 変数 in リスト 形式と、bash 専用の for ((初期化; 条件; 更新)) 形式の2種類があります。配列の全要素を反復するときは "${配列名[@]}" とダブルクォートで囲んでスペースを含む要素の分割を防ぎます。コマンド置換($())を使うと seq や find の出力を直接リストとして反復できますが、スペースを含むファイル名には対応できません。ファイルを反復する場合はグロブ展開(*.txt など)を使うほうが安全です。break でループを即座に終了し、continue で現在の反復をスキップして次へ進めます。条件分岐と組み合わせる場合は if 文 も合わせて参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。