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」を指定して実行する。