こんにちは。よっしーです(^^)
今日は、Bashスクリプトでwhile文を使用したときに最終行だけ処理をされなかった問題についてご紹介します。
背景
下記のデータファイル(sample.dat)があったとします。
1
2
3
このとき、下記のBashスクリプト(sample.sh)を起動します。
#!/bin/bash
while read line
do
echo ${line}
done < sample.dat
すると、実行結果は下記のようになりました。
% /bin/sh sample.sh
1
2
最終行の3がなぜか出力されないという問題が発生しています。
解決策
案1
sample.datの3の後に空行をいれる。
1
2
3
sample.shを起動します。
% /bin/sh sample.sh
1
2
3
期待した結果になりました。ただ、これだと、毎回datファイルの最終行に空行が入っているかどうかを確認しないといけないので、最終行に空行がはいっていなくても期待した結果になるようにsample.shを修正したいと思います。
案2
sample.shを下記のように修正します。
#!/bin/bash
while read line || [ -n "${line}" ]
do
echo ${line}
done < sample.dat
sample.shを起動します。
1% /bin/sh sample.sh
1
2
3
期待した結果になりました!
解説
#!/bin/bash
while read line || [ -n "${line}" ]
do
echo ${line}
done < sample.dat
このスクリプトは、Bashシェルスクリプトで書かれています。以下にコードの解説をします。
#!/bin/bash
この行は、スクリプトをBashシェルで実行することを指定しています。
while read line || [ -n "${line}" ]
この行は、ループを開始します。read
コマンドを使って変数line
に1行ずつ入力を読み込みます。||
演算子は、前のコマンドが失敗した場合に次のコマンドを実行することを意味します。[ -n "${line}" ]
は、変数line
が空でないかどうかをチェックする条件です。つまり、読み込んだ行が空でない限り、ループは続行されます。
do
echo ${line}
done
この部分は、ループの本体です。echo
コマンドを使用して、変数line
の値を出力します。${line}
は変数line
の値を参照するための構文です。
< sample.dat
この部分は、ファイルsample.dat
からの入力をループに渡すためのリダイレクションです。<
演算子は、ファイルからの入力を示します。
したがって、このスクリプトは、sample.dat
というファイルから1行ずつ読み込み、それぞれの行を表示するという動作を行います。ループは、ファイルの終端に到達するまで続きます。
最終行が出力されない理由
read
コマンドは、デフォルトでは改行文字を区切り文字として扱います。つまり、改行文字がない行は入力として認識されず、読み飛ばされます。
while read line
の行でread
コマンドを呼び出すと、read
コマンドは標準入力から1行ずつ読み込みます。その際、改行文字を区切り文字として使用します。もし改行文字がない行が現れると、read
コマンドは終端に到達せずにブロックされ続け、次の行の読み込みを試みます。そのため、改行文字がない行は無視され、表示されません。
ただし、ループを終了させるためには、入力からの読み込みが終了する必要があります。そのためには、ファイルの終端に到達する必要があります。ファイルの終端に到達した場合でも、-n "${line}"
の条件があるため、最後の行が空でない場合はループが続行されます。
したがって、改行文字がない行が表示されないのは、read
コマンドのデフォルトの動作によるものです。
改行がない行の read line の結果がfalseのために、while文の処理が実行されないのが原因のようです。
ただ、$lineにはデータが入っているので、$lineのデータの有無を条件に付け加えて対応したのが、今回の解決案になります。
おわりに
今日は、Bashスクリプトでwhile文を使用したときに最終行だけ処理をされなかった問題についてご紹介しました。
何か質問や相談があれば、遠慮なくコメントしてください。また、エンジニア案件についても、いつでも相談にのっていますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント