while 文の使用方法
while 文とは?
while 文は「ある条件が成り立っている間のみ繰り返し処理を実行する」といった、不定回の繰り返し処理を行う場合に使用するループ制御文である。
一般的に処理回数が明確である場合には for 文を用いるが、処理回数が開始時点では不明確な場合はこの while 文を用いる。
while 文は始めに指定された条件式の終了ステータスを判定し、結果が真である場合のみループ処理を継続する。ループ毎に条件式を評価し真であれば処理を実行する。これを繰り返し、条件式が偽になった時点でループ処理をを終了する。
while 条件式
do
処理
done
→ 処理回数が開始時点では不明確なループ処理には while 文を使用する。
while 文にはループの継続条件となる条件式を指定する。条件式には test コマンドを使用するのが一般的だが、当然その他のコマンドも使用可能である。
while 文により条件式に指定したコマンドが実行され、その終了ステータスが「0」、つまり真である場合のみループが継続される。
while 文の使用例
一般的な使用方法 1 - 条件式に test コマンドを使用する
while [ 条件式 ]
do
…
done
→ if 文と同じように []
を使用して条件式を指定する。
おそらくこれがもっとも一般的な while 文の継続条件を指定する方法だと思う。test コマンドの略式記述方法である []
の使用方法は「test コマンド」を参照してほしい。
実際に while 文を使用して、キーボードから入力された文字が “a” である間のみ処理を続けるシェルスクリプト (while_a.sh) を作成してみる。
#!/bin/bash
read key
while [ "$key" = "a" ]
do
echo "ok"
read key
done
このシェルスクリプト while_a.sh の実行結果は、以下のとおりとなる。
$ ./while_a.sh
b
$
#↑最初に "a" が以外が入力されると、継続条件が偽になるのでループ処理は1度も行われずにに終了する。
$ ./while_a.sh
a
ok
a
ok
a
ok
a
ok
b
$
“a” が入力されている間は条件式が [ "a" = "a" ]
となるので、結果は真となりループ内の処理が実行される。
また、“b” が入力された時点で、条件式が [ "b" = "a" ]
となり、結果が偽となるために while ループが終了する。
一般的な使用方法 2 - 条件式に test 以外のコマンドを使用する
今度は条件式に test コマンドではなく、そのほかのコマンドを用いた while ループを作ってみる。
while read line
do
echo "$line"
done <test.txt
# while文にリダイレクションするには done の直後に指定する
上記の例では while 文への入力に test.txt を指定している。while 文にはこのテキストファイルから1行ずつ自動で入力され、条件式に指定した read コマンドがそれを変数 line に格納している。
$ cat test.txt
111
222
333
444
555
#↑入力用のテキストファイルを用意する。
$ while read line
> do
> echo "$line"
> done <test.txt
111
222
333
444
555
$
#↑ファイルは 5 行目までしか無いため、6 回目の判定は偽となりループは 5 回で終了する。
上記例の while ループは始めに read line
が実行され、変数 line に標準入力からの値が設定される。
通常、標準入力はキーボードから入力だが、今回はリダイレクション(<
) でテキストファイル test.txt からの入力に切り替えられている。そのため 1回目のループではテキストファイルの 1行目「111」が変数 line に設定される。
正常に read コマンドが実行されたため、コマンドの終了ステータスが真となることで条件式は真となり、while ループ内の echo コマンドが実行される。
その後もテキストファイルから入力が続きループが継続されが、使用したテキストファイルは 5行目までしかないので、最終行の「555」を出力後に read コマンドが入力値なしのため失敗となる。それによって read コマンドの終了ステータスが 1 となり、条件式が偽となることで while ループが終了する。
一般的な使用方法 3 - 無限ループ
while :
do
…
if 条件式; then
break
fi
…
done
→ while 文の条件式にヌルコマンド (:
) を指定し、break コマンドを実行する処理を記述する。
while 文の条件式にヌルコマンド (:
) を指定することで、無限ループを作成することができる。
ヌルコマンドは終了ステータスが常に真となるため、while ループは終了することがなく無限ループとなる。
ループを抜けるには Ctrl+c で強制的に終了するか、while 文中に break コマンドを実行する判定文を記述する。
参考 - ヌルコマンドとは?
ヌルコマンドとは :
で表され、何も処理を行わずに終了するコマンドである。何も処理を行わないので、終了ステータスは常に真となる。
参考 - break コマンドとは?
break コマンドとは for 文や while 文、until 文で使用されるループを抜けるためのコマンドである。このコマンドが実行されるとループの途中であっても、その時点でループは終了となる (do ~ done の外に出る、つまり done の直後から再開される)。
通常、if 文と共に用いられ、「ある条件が成立したら実行しループを抜ける」といった使われ方をする。
下記は無限ループを break コマンドを使用して抜けるシェルスクリプト (while_endless.sh) の例。
#!/bin/bash
while :
do
read key
if [ "$key" = "q" ]; then
echo "breakコマンドを実行します..."
break
else
echo "$keyが入力されました。"
fi
done
echo "無限ループを抜けました。"
exit 0
このシェルスクリプト while_endless.sh の実行結果は、以下のとおりとなる。
$ ./while_endless.sh
a ←キー入力
aが入力されました。
b ←キー入力
bが入力されました。
c ←キー入力
cが入力されました。
q ←キー入力
breakコマンドを実行します...
無限ループを抜けました。
$
このシェルスクリプトは「q」が入力されるまで同じ処理が繰り返される。「q」が入力されると break コマンドが実行され、ループを終了する。
while ループを終了した後は while 文の done の直後から処理が継続される。
while 文中の if 文をさらに拡張すると、さまざまな条件でループを継続または終了することができる。つまり無限ループは while 文に指定する条件式では表現しきれないような、複雑な終了条件を指定したい場合に使用するとよい。
ループを制御する
break コマンドでループを途中で終了する
while 条件式
do
…
break
…
done
→ break コマンドを実行することでループを途中で終了することができる。
ループの途中でエラーが発生した場合など、ループを強制的に終了させたいときには break コマンドを実行する。無限ループを終了させたい場合も同様に、この break コマンドを使用する。
また、break コマンドに引数を指定することで、ネストされたループから一気に抜け出すことも可能である。
実際に break コマンドに引数を渡して、ネストされたループを一気に抜けるシェルスクリプト (while_break.sh) を作成してみる。
#!/bin/bash
while :
do
while :
do
read key
if [ "$key" = "q" ]; then
# 引数に指定された値を break コマンドに指定
break $1
fi
done
# $1 が2以上ならばここは出力されないはず
echo "2以上ならばここは実行されないはず。"
break
done
このシェルスクリプト while_break.sh の実行結果は、以下のとおりとなる。
$ ./while_break.sh 1
a
b
c
q
2以上ならばここは実行されないはず。
#↑引数が 1 だったため、一気にループを抜けられずに echo コマンドが実行された。
$ ./while_break.sh 2
a
b
c
q
#↑引数が 2 だったので、2重のループを一気に抜けたために、echo コマンドは実行されなかった。
$ ./while_break.sh 3
a
b
c
q
#↑引数が 3 でも 2 の場合と同じ。
シェルスクリプトでネストされたループを必要とするような機会はほとんどないと思われるが、break コマンドに引数を指定して、多重ループを一気に抜けるテクニックは覚えておいて損はない。
continue コマンドでループをスキップする
while 条件式
do
…
continue
…
done
→ continue コマンドを実行することで今回の処理をスキップし、ループの先頭に移動することができる。
ループ処理において、ある条件の場合のみ処理を行わずにスキップしたいときには、continue コマンドを実行する。
break コマンドと同様に、引数を指定することにより、ネストされたループ処理を一気にスキップすることが可能だ。
continue コマンドに引数を渡して、ネストされたループを一気にスキップするシェルスクリプト (while_continue.sh) を作成してみる。
#!/bin/bash
# 初回 exit 回避用フラグ
SKIP="ON"
while :
do
if [ "$SKIP" != "ON" ]; then
echo "continue 2 が実行されました."
exit 0
fi
# 以降のループでは上の処理をスキップしない
SKIP=""
while :
do
if [ "$CNT" = "ON" ]; then
echo "continue が実行されました."
continue 2
fi
# continue フラグを立てる
CNT="ON"
continue
# continue フラグをオフにする
CNT=""
done
done
このシェルスクリプト while_continue.sh の実行結果は、以下のとおりとなる。
$ ./while_continue.sh
continue が実行されました.
continue 2 が実行されました.
最初のメッセージは CNT フラグを立てた直後に、continue を実行したために出力されている。そのメッセージ出力直後に、今度は continue 2
が実行されて、処理がひとつ上の while ループの先頭に移動している。
SKIP フラグは ネストされたループに入る直前でオフにされているので、continue 2
実行後はメッセージを出力後に exit している。
多重ループの制御
break NUM
continue NUM
→ break コマンドと continue コマンドは引数に数値を指定することにより、ネストされた多重ループを越えた移動が可能になる。
引数に指定した数値の分だけ上の階層のループを対象に実行される。引数を省略した場合は、「1」を指定したのと同じ動作になる。
while :
do
# 1つ目のループ
・・・
while :
do
# 2つ目のループ
・・・
# 1つ目のループの先頭に戻る。
continue 2
・・・
# 1つ目のループから抜ける。
break 2
・・・
done
done
上記のような2重ループから抜け出すには、 break コマンドの引数に「2」を指定して実行する。同様に2重ループの先頭 (1行目の while ループ先頭) に戻るには、 continue コマンドの引数に「2」を指定して実行する。