Bashのwhile文の書き方: ループ文で繰り返し処理を行う
目次
Bashのwhile文の書き方
Bash(バッシュ)にはループ文というループ処理を書ける文が複数あります。
そのループ文の1つにwhile
(ホワイル)文があります。
このwhile
文は繰り返し処理をしたい時に使うことが出来ます。
たとえばwhile
文は↓のように書きます。
i=0 while [ $i -ne 4 ]; do echo $i ((i++)) done # 0 # 1 # 2 # 3
この記事ではBashのwhile
文について具体的に↓を見ていきます。
ループ文とは?
for文とwhile文の違い
while文の構造
判定部分の式の正体
ファイルの内容を1行ずつ読み込む
無限ループ
使用例
問題
ループ文とは?
そもそもループ文とはいったいなんなのか? という話です。
プログラミングにおけるループ文とは、繰り返し処理を行うための文です。
繰り返し処理とは、特定の処理を繰り返し行うということを言います。
(^ _ ^) | そのまんまですね |
たとえば↓のような処理があったとします。
処理1 処理2
これらの処理、処理1と処理2を繰り返すとどうなるのかと言うと、
処理1 処理2 処理1 処理2 処理1 処理2 ...
↑のように処理1と処理2のまとまりが繰り返し行われるようになります。
この繰り返しには回数を設けることも可能です。
つまり、10回繰り返すとか、100回繰り返すとか、あるいは無限に繰り返すとか、そういった回数を指定することが可能だということです。
プログラミングにおけるループ文は、プログラムと言えばこれ! というほどメジャーな機能です。
代表的なループ文にはfor
(フォー)文やwhile
(ホワイル)文などがあります。
今回の記事で紹介するのはこの内のwhile
文のほうです。
for文とwhile文の違い
ループだけに繰り返しになりますが、ループ文にはfor
文とwhile
文があります。
これらの2つのループ文の違いについて解説します。
まずこれら2つのループ文の役割ですが、基本は同じです。
この2つのループ文の目的は、特定の処理を繰り返すことです。
なぜ2つのループ文に分かれているのかと言うと、これら2つは文の構造が違います。
for
文のほうの構造は「初期化・判定・処理・更新」に分かれています。
いっぽうwhile
文のほうの構造は「判定・処理」に分かれています。
つまりwhile
文のほうがfor
文よりシンプルなんですね。
while
文のほうがシンプルなだけあってコーディング量もwhile
文のほうが少ないです。
そのためプログラミングのシーンによってこれらの2つのループ分をかき分けることでコーディング量を減らすことができます。
コーディング量はプログラミングにおける「疲労の度合い」と直結する重要な開発者にとっての関心事です。
while
文はこの労力を減らすことができるという点で価値があります。
また、シンプルなだけあってwhile
文はfor
文に比べるとわかりやすい構造になっているため、学習コストもfor
文と比べると少なくなっています。
while文の構造
while
文の構造について解説します。
while
文は↓のような構造になっています。
while 判定; do 処理 done
まずキーワードとして「while
」を書き、そのあとに判定を行う式を書きます。
セミコロン(;
)のあとにキーワード「do
」を書き、その後に繰り返す処理を書きます。
繰り返し処理を書き終わったら最後にwhile
文の終端としてキーワード「done
」を書きます。
ちなみにセミコロン(;
)を付けない場合は↓のようにも書けます。
while 判定 do 処理 done
while
文は「判定」の式の結果がtrue
の間、ループを繰り返します。
判定がfalse
になったらその時点でループを終了します。
「判定」部分には式を書くことができます。
式とはたとえば↓のような式です。
i=0 while [ $i -eq 0 ]; do echo "0です。" done
[ $i -eq 0 ]
という部分が式です。
この式では変数i
の値が0
と等しいかどうか比較しています。
変数i
の値は0
なので、この比較は常にtrue
になります。
よって↑のwhile
文の判定は常にtrue
になるので、延々とループ処理が実行されることになります。
「0です。」という出力が無限に出力されるため、↑のコードを実行した場合はCtrl+C
などでスクリプトを終了してください。
判定部分の式の正体
このようにwhile
文には判定部分に式を使うことができます。
この判定部分の式は正確にはtest
コマンド、または[
コマンドです。
角カッコ([]
)の[
はコマンドで、test
コマンドとほぼ同じ挙動をするコマンドです。
[
コマンドは最後の引数の]
を無視するため、角カッコで式を囲んだような表記が可能になります。
$i -eq 0
の部分は[
コマンドの引数です。
[
コマンドはこれらの引数を評価して返り値、つまり終了ステータスを返します。
ためしに↓のコードを実行してみてください。
i=0 [ $i -eq 0 ] echo $? [ $i -eq 1 ] echo $?
コマンドの終了ステータスは$?
変数で参照することが可能です。
↑のように[
コマンドを実行した後にecho
で$?
を参照すると、↓の結果になります。
0 1
これはtest
コマンドの挙動と同じです。
試しにtest
コマンドを↓のように実行してみます。
i=0 test $i -eq 0 echo $? test $i -eq 1 echo $?
結果は↓になります(先ほどと同じです)。
0 1
test
コマンドと[
コマンドの大きな違いは、先ほども述べましたが最後の]
を無視するかしないかの違いです。
test
コマンドは最後の]
を無視しないので、たとえば↓のようなコマンドはエラーになります。
i=0 test $i -eq 0 ]
出力結果↓。
sample.sh: line 2: test: too many arguments
test
コマンドはタイプ数が多いため、while
文などでは[
コマンドのほうが使われることが多いです。
しかしもちろん↓のようにtest
コマンドをwhile
文に使うこともできます。
i=0 while test $i -eq 0; do echo "iは0です。" done
角カッコを使った表記のほうが人の視覚的に見やすいため、一般的には[
のほうが多用されます。
しかしtest
コマンドでも代用が効くというところは頭の片隅にでも置いておいてください。
ファイルの内容を1行ずつ読み込む
while
文でファイルの内容を1行ずつ読み込みたい場合は↓のようなフォーマットになります。
while read 変数名; do 処理 done < ファイル名
キーワードの「while
」のあとにread
コマンドを書きます。
read
コマンドはBashの組み込みコマンドで、標準入力からの入力を1行読み込んで、引数の変数に格納します。
セミコロン、do
と処理内容、done
キーワードを書きます。
done
キーワードのあとに小なり(<
)を書き、読み込ませたいファイル名を書きます。
↑の「ファイル名」の部分のファイルがオープンされ、その内容が一行ずつread
コマンドによって読み取られます。
読み込んだ内容はread 変数名
の「変数名」に格納され、それをdo
~done
の間の処理部分で使うことができます。
たとえばanimals.txt
というファイルを読み込みたい場合は↓のようにします。
while read name; do echo $name done < animals.txt
実行結果。
cat dog bird
animals.txt
というファイルは↓のように動物の名前が英語で一行ずつ羅列されているファイルです。
cat dog bird
このファイル内の行がread
コマンドで一行ずつ読み取られていくわけですね。
read name
としているので、読み込んだ行は$name
で参照することが可能です。
↑のコードでは$name
をecho
で参照して出力しています。
無限ループ
while
文の無限ループは↓のように書きます。
while true; do 処理 done
Bashではtrue
コマンドやfalse
コマンドを使うことができます。
これ、コマンドなんです。
true
コマンドはなにもしませんが、終了ステータスを0
で返します。
false
コマンドは終了ステータスを1
で返します。
true echo $? false echo $?
実行結果↓。
0 1
while
文の判定部分にこれらtrue
コマンドやfalse
コマンドを使うことができます。
true
コマンドを使った場合は、その返り値(終了ステータス)が判定に使われます。
true
コマンドは常に終了ステータス0
を返すため、while
文でtrue
コマンドを使うと無限ループになります。
↓は1秒ごとに時刻を表示するサンプルです。
while true; do date sleep 1 done
使用例
Bashのwhile
文の使用例です。
カウント変数をカウントする
カウント変数を0から4までカウントするサンプルです。
i=0 while [ $i -lt 4 ]; do ((i++)) done echo $i # 4
[ $i -lt 4 ]
という[
コマンドは、変数$i
の値が4
より低いかどうか評価しています。
変数$i
は最初に0
で初期化され、while
文の繰り返し処理の中で((i++))
でインクリメントされます。
変数$i
の値が4
以上になったら[ $i -lt 4 ]
の結果はfalse
, つまり1
になるので、while
文が終了します。
while
文が終了した時点で変数$i
の値は4
になっているので、これをecho
で出力しています。
CSVファイルを読み込み再構成して表示する
CSVファイルから1行ずつ読み込んでデータを再構成して表示します。
while read line; do echo $line | awk -F, '{print "名前", $1, "年齢", $2, "体重", $3}' done < animals.csv # 名前 cat 年齢 10 体重 30 # 名前 dog 年齢 20 体重 40 # 名前 bird 年齢 70 体重 4
animals.csv
ファイルは↓のようなフォーマットのファイルです。
cat,10,30 dog,20,40 bird,70,4
先頭の列から名前、年齢、体重を表しています。
このファイルをwhile
文のread
コマンドで1行ずつ読み込みます。
echo $line | awk -F, '{print "名前", $1, "年齢", $2, "体重", $3}'
というコマンドは、行の内容を標準出力に出力し、awk
に渡しています。
awk
ではCSVのフォーマットの文字列を分解し、その列を$1
, $2
, $3
で参照しています。
awk
のprint
でそれらの列をラベルを付けて出力しています。
問題
Q1: while
文の構成部品をすべてあげよ
判定と処理
初期化と判定と更新と処理
処理
Q2: while
文で1行ずつファイルを読み込む場合ファイル名はどこに指定するか答えよ
whileの後
doの後
doneの後
Q3: while
文で無限ループするときの判定部分として正しいものを答えよ
true
false
[ 0 -eq 1 ]
正解
Q1: 1
Q2: 3
Q3: 1