🐧 Linux 総合学習プラットフォーム
シェルスクリプト ・ 中級

繰り返し(for / while)

同じ処理を何度も行うには繰り返し(ループ)を使います。for は決まったリストを1つずつ処理するのに向き、for f in *.txt; do ... done のようにファイル群へまとめて処理できます。while は条件が真である間ずっと繰り返し、while read line; do ... done < file のように1行ずつ読む用途で活躍します。途中で抜けるには break、次の周回へ飛ばすには continue を使います。

同じような処理を何度も繰り返したいとき、コマンドを何行もコピーして並べるのは現実的ではありません。そこで使うのが繰り返し(ループ)です。たくさんのファイルに同じ加工をする、決まった回数だけ処理を回す、ファイルを1行ずつ読んで順に処理する——こうした作業は、ループを使えば数行で表現できます。bash の代表的なループは for と while の2つで、それぞれ得意とする場面が違います。両者の性格を理解して使い分けられると、スクリプトで自動化できる仕事の幅が大きく広がります。

for ループ — 決まったリストを1つずつ

for は「あらかじめ決まったリストの要素を、先頭から1つずつ取り出して繰り返す」のに向いたループです。基本形は for 変数 in リスト; do 処理; done で、in の後ろに並べた値を変数に順に入れながら、do から done までを繰り返します。たとえば for fruit in りんご みかん ぶどう; do echo "果物: $fruit"; done と書くと、3つの果物を順に表示します。実務で特に強力なのが、ファイル名のパターン(ワイルドカード)と組み合わせる使い方です。for f in *.txt; do echo "$f"; done とすれば、カレントディレクトリにある拡張子 .txt のファイルすべてに対して、まとめて同じ処理を施せます。連続した数値を回したいときは seq コマンドが便利で、for i in $(seq 1 5); do echo "回数 $i"; done のようにコマンド置換と組み合わせれば、1から5までの繰り返しになります。bash には for ((i=1; i<=5; i++)); do ... done という、C言語に似た数え上げの書き方もあります。

while ループ — 条件が真である間ずっと

while は「指定した条件が成り立つ(真である)間、ずっと繰り返す」ループです。基本形は while 条件; do 処理; done で、繰り返すたびに条件を確かめ、成り立たなくなった時点でループを抜けます。条件の判定には、if 文と同じく [ ] や test を使います。カウンタを使った例を見ると動きが分かりやすいでしょう。i=1; while [ "$i" -le 3 ]; do echo "i は $i"; i=$((i + 1)); done と書くと、i が3以下である間だけ繰り返し、i は1、2、3と表示されます。ここで i=$((i + 1)) は算術展開 $(( )) を使ってカウンタを1増やす処理です。while で繰り返しの回数を制御するときは、このようにループの中で必ずカウンタを更新する必要があります。

while read で1行ずつ読む

while が真価を発揮するのが、ファイルやコマンド出力を1行ずつ処理する場面です。while read line; do 処理; done < file という形が定番で、ファイルの内容を上から1行ずつ変数 line に読み込みながら繰り返します。末尾の < file はリダイレクトで、file の中身をループの標準入力として流し込んでいます。たとえば設定ファイルを1行ずつ点検したり、リスト化されたサーバ名を順に処理したりするのに使います。read は読み込む行が無くなると失敗(終了ステータス0以外)を返すので、ファイルの最後まで来ると自然にループが終わります。コマンドの出力を1行ずつ処理したいときは、ファイルの代わりにパイプを使って somecommand | while read line; do ...; done と書くこともできます。

break と continue で流れを制御する

ループの途中で繰り返しを打ち切ったり、一部の周回だけ飛ばしたりしたいときは、break と continue を使います。break は、その時点でループ全体を即座に抜けます。たとえば探していた値が見つかった瞬間に、それ以上回す必要がなければ break で抜ける、という使い方をします。一方 continue は、現在の周回の残りの処理をスキップして、すぐ次の周回へ進みます。条件に合わない要素だけ処理を飛ばして次へ行きたいときに便利です。たとえば for f in *.txt; do if [ ! -s "$f" ]; then continue; fi; echo "$f を処理"; done と書けば、中身が空のファイル(-s は「サイズが0より大きい」を意味し、! で否定)は処理をスキップし、中身のあるファイルだけを処理できます。

無限ループとよくある失敗

ループで最も気を付けたいのが無限ループです。while ループでカウンタの更新を書き忘れると、条件がいつまでも真のままになり、繰り返しが永遠に止まりません。先ほどの例で i=$((i + 1)) を書き忘れると、i はずっと1のままなので i は1が延々と表示され続けます。もしこの状態に陥ってしまったら、Ctrl + c を押せば実行中のループを強制的に止められます。もうひとつ気を付けたいのが、for f in *.txt のようなワイルドカードを使う場合に、条件に合うファイルが1つも無いときの挙動です。bash の既定では、一致するものが無いと *.txt という文字列がそのまま変数に入ってしまい、存在しないファイル名を処理しようとして妙な結果になることがあります。処理の冒頭で対象ファイルの有無を確かめる、あるいはファイルが存在するかを if で点検してから本処理に進む、といったひと手間を入れると、こうした想定外を避けられます。「for は決まったリストを1つずつ、while は条件が続く間ずっと」という性格の違いを押さえ、カウンタの更新と終了条件、そして break と continue による流れの制御を意識すれば、ループは安全で強力な道具になります。多数のファイルへの一括処理や、ログを1行ずつ点検する定型作業など、手作業では骨の折れる仕事を、ループは数行で確実にこなしてくれます。

この項目に出てくる用語

ループるーぷ
同じ処理を繰り返す制御構造。for と while が代表的。

関連コマンド

echoread

▶ 学習アプリでこの続きを学ぶ・演習する