Linuxでのwhile(bash)の使い方です。条件が真である間コマンドを繰り返すループの構文のひとつになります。
whileコマンドは例えば、ファイルをreadコマンドで一行ずつ読み込んで、処理するようなときに利用できます。
whileの構文
基本的なwhileの構文
1 |
while test-commands; do consequent-commands; done |
基本的なwhileのコマンド
基本的なwhileコマンドの構文は以下のようになります。
1 |
while 条件のコマンド; do 繰り返すコマンド; done |
条件について、シェルでは0のときに真に、それ以外の時が偽になります。つまり、条件のコマンドの返り値が0のとき、コマンドが繰り返し実行されます。
シェルスクリプトでは
1 2 3 4 |
while 条件 do 繰り返すコマンド done |
や
1 2 3 |
while 条件; do 繰り返すコマンド done |
のようによく記述されます。
breakコマンドとcontinueコマンド
ループを制御するコマンドとして、breakコマンドとcontinueコマンドがあります。
breakコマンドはループを抜け出したいときに利用できます。
break.sh
1 2 3 4 5 6 7 8 9 10 11 |
#!/bin/bash declare -i i=0 while true; do echo "i: $i" if (($i >= 3)); then break fi i+=1 done |
実行結果
1 2 3 4 5 |
$ ./break.sh i: 0 i: 1 i: 2 i: 3 |
また、breakコマンドの引数に数値を取ることがその数値だけループを抜けることができます。
break2.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#!/bin/bash declare -i i=0 declare -i j=0 while true; do while (( $j <= 3 )); do echo "i: $i j: $j" if (( $i == 2 )) && (( $j == 2 )); then break 2 fi j+=1 done i+=1 j=0 done |
実行結果
1 2 3 4 5 6 7 8 9 10 11 12 |
$ ./break2.sh i: 0 j: 0 i: 0 j: 1 i: 0 j: 2 i: 0 j: 3 i: 1 j: 0 i: 1 j: 1 i: 1 j: 2 i: 1 j: 3 i: 2 j: 0 i: 2 j: 1 i: 2 j: 2 |
continueコマンドはcontinueコマンドが実行された時点で次のループに移行するコマンドになります。
continue.sh
1 2 3 4 5 6 7 8 9 10 |
#!/bin/bash declare -i i=0 while (( $i < 10 )); do i+=1 if (( $i % 2 == 0 )); then continue fi echo "i: $i" done |
実行結果
1 2 3 4 5 6 |
$ ./continue.sh i: 1 i: 3 i: 5 i: 7 i: 9 |
また、continueコマンドも数値を引数に取ることができ、例えば、2重ループの場合に数値を指定することで最初のループから再開できます。
continue2.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#!/bin/bash declare -i i=0 declare -i j=0 while (( $i < 10 )); do i+=1 while true; do j+=1 if (( $j % 2 == 0 )); then echo "continue 2; i: $i j: $j" continue 2 fi if (( $i % 2 == 0 )); then echo "continue ; i: $i j: $j" continue fi echo "i: $i j: $j" done done |
実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ ./continue2.sh i: 1 j: 1 continue 2; i: 1 j: 2 continue ; i: 2 j: 3 continue 2; i: 2 j: 4 i: 3 j: 5 continue 2; i: 3 j: 6 continue ; i: 4 j: 7 continue 2; i: 4 j: 8 i: 5 j: 9 continue 2; i: 5 j: 10 continue ; i: 6 j: 11 continue 2; i: 6 j: 12 i: 7 j: 13 continue 2; i: 7 j: 14 continue ; i: 8 j: 15 continue 2; i: 8 j: 16 i: 9 j: 17 continue 2; i: 9 j: 18 continue ; i: 10 j: 19 continue 2; i: 10 j: 20 |
readコマンドと組み合わせる
readコマンドは標準入力から一行ずつ読み込み、その行を変数に代入できるコマンドになります。
readコマンドはEOFを読み込むまで、基本的に終了ステータスは0のため、whileコマンドと組み合わせて利用し、行を読み込んで何か処理を行うのにとても便利な組み合わせです。
例えば、以下のスクリプトはeを含む行を表示する例になります。
read.sh
1 2 3 4 5 6 7 8 |
#!/bin/bash pattern='.*e.*' while read line; do if [[ $line =~ $pattern ]]; then echo $line fi done |
data.txt
1 2 3 4 |
apple banana melon lemon |
実行結果
1 2 3 4 |
$ ./read.sh < data.txt apple melon lemon |
while文の展開されている内容を確認
setコマンドのxオプションをwhile文の展開されている内容を確認することができます。
1 |
set -x |
「set -x」を用いた場合、そのトレースされている内容はデフォルトでは'+'の文字列が最初についた行で表されます。これは変数PS4を設定することで変更できます。
実行例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
$ set -x $ i=0 + i=0 $ while (($i<5)); do echo $i; let i+=1; done + (( 0<5 )) + echo 0 0 + let i+=1 + (( 1<5 )) + echo 1 1 + let i+=1 + (( 2<5 )) + echo 2 2 + let i+=1 + (( 3<5 )) + echo 3 3 + let i+=1 + (( 4<5 )) + echo 4 4 + let i+=1 + (( 5<5 )) |
setコマンドの機能をオフしたい場合は
1 |
set +x |
を使用すれば、元に戻すことができます。
スクリプトに対して、トレースを行いたい場合はbashコマンドでxオプションをつけて実行しても良いかもしれません。
実行例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ bash -x read.sh < data.txt + pattern='.*e.*' + read line + [[ apple =~ .*e.* ]] + echo apple apple + read line + [[ banana =~ .*e.* ]] + read line + [[ melon =~ .*e.* ]] + echo melon melon + read line + [[ lemon =~ .*e.* ]] + echo lemon lemon + read line |