Bashプログラミング
 Computer >> コンピューター >  >> プログラミング >> Bashプログラミング

Linuxシェルスクリプトで使えるBash forループの書き方と実例12選

Linuxシェルスクリプトで使えるBash forループの書き方と実例12選Bashのforループには、大きく分けて2種類の書き方があります。1つは「in」キーワードの後に値のリストを指定する形式、もう1つはC言語のような構文を使用する形式です。

本記事は、当サイトで連載中のBashチュートリアルシリーズの一篇です。

ここでは両方のforループ方式を解説し、シェルスクリプトでforループを活用するための具体例を12個紹介します。

forループの使い方については、この記事1つで事足りるよう網羅的にまとめています。今後の参考にするためにも、ぜひブックマークしておいてください。

方法1:「in」キーワードと値のリストによるBash forループ

構文:

for varname in list
do
 command1
 command2
 ..
done

上記の構文における各要素の意味は次のとおりです。

  • for、in、do、doneはすべてキーワードです
  • 「list」には値のリストが入ります。スペースで区切った複数の単語を格納した変数を指定することも可能です。for文でリストを省略した場合は、シェルスクリプトに渡された位置パラメータが代わりに使用されます。
  • varnameには任意のBash変数名を指定します。

この形式では、for文はリスト内の項目ごとに1回ずつ、do〜doneの間のコマンドを実行します。例えばリストに5つの項目があれば、ループは合計5回実行されます。ループを1周するごとに、リストから取り出した現在の項目が変数「varname」に格納され、ループ本体内でその値を自由に処理できます。

方法2:C言語風の構文によるBash forループ

2つ目の形式は、C言語のforループによく似ており、「初期化」「条件」「更新」という3つの式で構成されます。

for (( expr1; expr2; expr3 ))
do
 command1
 command2
 ..
done

この構文の動作は以下のとおりです。

  • 最初の反復の前にexpr1が評価されます。通常はループ用変数の初期化に使用します。
  • do〜done間のステートメントは、expr2の評価結果がTRUEである限り繰り返し実行されます。
  • ループの各反復が終わるたびにexpr3が評価されます。通常はループカウンタの増加に使用します。

以降では、これらのforループをさまざまな場面で使いこなすための12の例を紹介します。

1. 「in」の後に静的な値のリストを直接指定する

次の例では、値のリスト(Mon、Tue、Wed、Thu、Fri)を「in」キーワードの後に直接記述しています。

$ cat for1.sh
i=1
for day in Mon Tue Wed Thu Fri
do
 echo "Weekday $((i++)) : $day"
done

$ ./for1.sh
Weekday 1 : Mon
Weekday 2 : Tue
Weekday 3 : Wed
Weekday 4 : Thu
Weekday 5 : Fri

注意: 値のリストをカンマで区切ってはいけません(Mon, Tue, Wed, Thu, Fri)。カンマは値の一部として扱われてしまいます。つまり「Mon」ではなく「Mon,」という値になってしまうのです。実際の動作は下記の例をご覧ください。

$ cat for1-wrong1.sh
i=1
for day in Mon, Tue, Wed, Thu, Fri
do
 echo "Weekday $((i++)) : $day"
done

$ ./for1-wrong1.sh
Weekday 1 : Mon,
Weekday 2 : Tue,
Weekday 3 : Wed,
Weekday 4 : Thu,
Weekday 5 : Fri

注意: 値のリスト全体をダブルクォートで囲んでもいけません("Mon Tue Wed Thu Fri")。ダブルクォートで囲むと、5つの別々の値ではなく1つの値として扱われてしまいます。下記の例で確認できます。

$ cat for1-wrong2.sh
i=1
for day in "Mon Tue Wed Thu Fri"
do
 echo "Weekday $((i++)) : $day"
done

$ ./for1-wrong2.sh
Weekday 1 : Mon Tue Wed Thu Fri

2. 「in」の後に変数でリストを指定する

forループに値を直接書かずに、あらかじめ変数に格納しておき、「in」の後ろでその変数を参照することもできます。次の例を見てください。

$ cat for2.sh
i=1
weekdays="Mon Tue Wed Thu Fri"
for day in $weekdays
do
 echo "Weekday $((i++)) : $day"
done

$ ./for2.sh
Weekday 1 : Mon
Weekday 2 : Tue
Weekday 3 : Wed
Weekday 4 : Thu
Weekday 5 : Fri

注意: ベストプラクティスとしては、Bash変数を参照する際は常にダブルクォートで囲むべきですが、このルールにはいくつか例外があり、forループのリスト指定はまさにその1つです。ここで変数をダブルクォートで囲むと、値のリスト全体が1つの値として扱われてしまいます。多くの人が陥りがちな落とし穴なので注意してください。forループ内では変数をダブルクォートで囲まないようにしましょう。

$ cat for2-wrong.sh
i=1
weekdays="Mon Tue Wed Thu Fri"
for day in "$weekdays"
do
 echo "Weekday $((i++)) : $day"
done

$ ./for2-wrong.sh
Weekday 1 : Mon Tue Wed Thu Fri

3. リストを指定せず、位置パラメータから取得する

forループで「in」キーワードと値のリストを省略すると、位置パラメータ(つまりシェルスクリプトに渡された引数)が自動的に使用されます。

$ cat for3.sh
i=1
for day
do
 echo "Weekday $((i++)) : $day"
done

$ ./for3.sh Mon Tue Wed Thu Fri
Weekday 1 : Mon
Weekday 2 : Tue
Weekday 3 : Wed
Weekday 4 : Thu
Weekday 5 : Fri

注意: この方法を使う場合は慎重に行ってください。「in」キーワード自体を記述してはいけません。「in」だけを書いて値を省略すると、下記の例のように位置パラメータは使われず、ループ内部に入ることすらありません。つまりforループは一度も実行されないのです。

$ cat for3-wrong.sh
i=1
for day in
do
 echo "Weekday $((i++)) : $day"
done

$ ./for3-wrong.sh Mon Tue Wed Thu Fri

補足: Bashの位置パラメータについて詳しくは、以前の関連記事をご参照ください。

4. UNIXコマンドの出力をリストとして利用する

コマンドをバッククォート(` `)で囲むことで、任意のUNIX/Linuxコマンドの出力をforループの値のリストとして利用できます。

$ cat for4.sh
i=1
for username in `awk -F: '{print $1}' /etc/passwd`
do
 echo "Username $((i++)) : $username"
done

$ ./for4.sh
Username 1 : ramesh
Username 2 : john
Username 3 : preeti
Username 4 : jason
..

5. ファイルやディレクトリを順番に処理する

特定のディレクトリ配下のファイルやディレクトリをループ処理したい場合は、まずそのディレクトリにcdで移動し、forループで「*」を指定するだけです。

次の例では、ホームディレクトリ直下のすべてのファイルとディレクトリを順に処理しています。

$ cat for5.sh
i=1
cd ~
for item in *
do
 echo "Item $((i++)) : $item"
done

$ ./for5.sh
Item 1 : positional-parameters.sh
Item 2 : backup.sh
Item 3 : emp-report.awk
Item 4 : item-list.sed
Item 5 : employee.db
Item 8 : storage
Item 9 : downloads

forループで「*」を使うやり方は、Linuxコマンドラインでlsコマンドなどとともに使うファイルグロブ(ワイルドカード展開)と同じ考え方です。

例えば、次のコマンドはホームディレクトリ直下のすべてのファイルとディレクトリを表示します。上記のfor5.shの例はまさにこの仕組みを応用したものです。

cd ~
ls *

次のコマンドは、/etcディレクトリ配下にあるa、b、c、dのいずれかで始まる*.confファイルをすべて表示します。

$ ls -1 /etc/[abcd]*.conf
/etc/asound.conf
/etc/autofs_ldap_auth.conf
/etc/cas.conf
/etc/cgconfig.conf
/etc/cgrules.conf
/etc/dracut.conf

このlsコマンドで使った引数と同じものは、次の例のようにBashのforループでもそのまま使えます。

$ cat for5-1.sh
i=1
for file in /etc/[abcd]*.conf
do
 echo "File $((i++)) : $file"
done

$ ./for5-1.sh
File 1 : /etc/asound.conf
File 2 : /etc/autofs_ldap_auth.conf
File 3 : /etc/cas.conf
File 4 : /etc/cgconfig.conf
File 5 : /etc/cgrules.conf
File 6 : /etc/dracut.conf

6. breakでループを途中終了する

「break」コマンドを使うと、forループを途中で抜けることができます。

$ cat for6.sh
i=1
for day in Mon Tue Wed Thu Fri
do
 echo "Weekday $((i++)) : $day"
 if [ $i -eq 3 ]; then
   break;
 fi
done

$ ./for6.sh
Weekday 1 : Mon
Weekday 2 : Tue

7. continueでループの先頭に戻る

特定の条件下で、ループ内の残りのコマンドをスキップし、次の値の処理から再開したい場合には「continue」コマンドを使用します。

次の例では、SatとSunには「(WEEKEND)」を付け、それ以外の曜日には「(weekday)」を付けて表示しています。

$ cat for7.sh
i=1
for day in Mon Tue Wed Thu Fri Sat Sun
do
 echo -n "Day $((i++)) : $day"
 if [ $i -eq 7 -o $i -eq 8 ]; then
   echo " (WEEKEND)"
   continue;
 fi
 echo " (weekday)"
done

$ ./for7.sh
Day 1 : Mon (weekday)
Day 2 : Tue (weekday)
Day 3 : Wed (weekday)
Day 4 : Thu (weekday)
Day 5 : Fri (weekday)
Day 6 : Sat (WEEKEND)
Day 7 : Sun (WEEKEND)

8. C言語風の構文でBash forループを書く

この例では、C言語のforループに近い2つ目の方式を使用しています。次のスクリプトは、Cスタイルのforループで乱数を5個生成します。

$ cat for8.sh
for (( i=1; i <= 5; i++ ))
do
 echo "Random number $i: $RANDOM"
done

$ ./for8.sh
Random number 1: 23320
Random number 2: 5070
Random number 3: 15202
Random number 4: 23861
Random number 5: 23435

9. 無限ループを作る

Cスタイルのforループで開始値・条件・増分をすべて省略すると、無限ループになります。ループを止めるにはCtrl-Cを押してください。

$ cat for9.sh
i=1;
for (( ; ; ))
do
   sleep $i
   echo "Number: $((i++))"
done

前述のとおり、このBashの無限forループから抜けるにはCtrl-Cを押します。

$ ./for9.sh
Number: 1
Number: 2
Number: 3

10. Cスタイルforループでカンマを使う

BashのCスタイルforループでは、条件に使う値の増加だけでなく、別の変数も同時に増加させることができます。

初期化セクションと増分セクションでは、カンマで区切ることで複数の値を扱えます。

次のforループは変数iを使って合計5回実行されます。一方、変数jは5から始まり、ループが1回実行されるごとに5ずつ増えていきます。

$ cat for10.sh
for ((i=1, j=10; i <= 5 ; i++, j=j+5))
do
 echo "Number $i: $j"
done

$ ./for10.sh
Number 1: 10
Number 2: 15
Number 3: 20
Number 4: 25
Number 5: 30

11. ブレース展開で数値の範囲を指定する

「in」の後ろでブレース展開({})を使うと、数値の範囲を指定してループ処理ができます。

次の例では、1から10までの値を使って10回ループを実行します。

$ cat for11.sh
for num in {1..10}
do
 echo "Number: $num"
done

$ ./for11.sh
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
...

12. 増分を指定した数値の範囲でループする

次の例では、1から10までの値を増分2で5回ループします。つまり1から始まり、2ずつ増加しながら10に達するまで処理を繰り返します。

$ cat for12.sh
for num in {1..10..2}
do
 echo "Number: $num"
done

$ ./for12.sh
Number: 1
Number: 3
Number: 5
Number: 7
Number: 9

PS: 今後の参考のため、この記事のブックマークをお忘れなく。

  1. BashのPS1変数を極める:機能的でおしゃれなLinuxプロンプトを作る10の実例

    Photo courtesy of f1r3storm85 前回の記事では、Linuxの環境変数であるPS[1-4]とPROMPT_COMMANDについて解説しました。PS1を効果的に活用すれば、コマンドプロンプト上に直接、有用な情報を表示させることができます。 映画『トゥームレイダー』のアンジェリーナ・ジョリーは、謎をスタイリッシュに解き明かすために、あらゆるガジェットや武器を自由に操ります。彼女の装備やスタイルに完全に匹敵するのは難しいとしても、せめて普段使いのLinuxプロンプトくらいは、この記事で紹介する10の実例を使って、高機能かつおしゃれにカスタマイズしてみてはいかがでしょうか。

  2. 実用的な5つの例で学ぶBashスクリプト入門チュートリアル

    Unix SedやUnix Awkシリーズと同様に、本シリーズではBashスクリプティングに関する記事を複数回にわたって公開します。実用的なサンプルを交えながら、Bashスクリプティングのテクニックを幅広く解説していきます。 シェルとは、ユーザーが入力したコマンドを解釈して実行するプログラムです。コマンドはユーザーが直接入力するか、「シェルスクリプト」と呼ばれるファイルから読み込まれます。 ユーザーからの入力を直接読み取る場合、そのシェルは対話型(インタラクティブ)シェルと呼ばれます。 一方、ファイルからコマンドを読み込んで実行する場合は非対話型(ノンインタラクティブ)シェルと呼ばれます。この