はじめに
シェルスクリプトでファイルの各行に対して複雑なロジックを書かざるを得ない場合、
while read を使って一行ずつ読み込む処理を騎獣します。
echo や cat を使ってファイルを作成した場合はファイルの末尾に必ず改行コード(LF)が付与されますが、
少々変わったファイルの作り方をすると改行コードが付与されないことがあります。
(どきどきチームで開発しているソースコードにもこういうファイルが紛れ込むケースもありますね。)
そんなファイルを while read する場合に最終行がループ処理の対象にならない、とハマりがちなので対処法をまとめます。
検証環境
| |
普通に作成したファイルを while read して一行ずつ読み込んでみる
普通に作成したファイルを while read して一行ずつ読み込む場合、
以下のようなコードになるかと思います。
( IFS= を付与しないと、 read 時に行頭・行末のスペースが除去されてしまいます。 -r オプションがないと各行末に \ があった場合におかしな挙動をしてしまいます。 )
| |
問題なく3行とも読み込めました。
ファイルの最終行に改行コード(LF)が含まれないファイルを while read した場合
今度はファイルの最終行( ccc )に改行コードが付与されないようにファイルを作成した後、
while read してみます。
| |
ccc の行が出力されません。
ループの処理対象となっていないことがわかります。
原因を噛み砕いてみる
原因をもう少し噛み砕いてみます。
read コマンドは while でループせずとも利用できます。
echo の実行結果を read で読み込む例を見ていきます。
まずは普通に echo した結果を read コマンドに読み込ませた場合の実行結果を見てみます。
| |
実行ステータスは 0 、 変数 $L の値は 111 となっています。
echo で渡された文字列を読み込み、変数に代入できていることがわかります。
次に echo -n として、行末に改行コードを出力させなかった場合の実行結果を見てみます。
| |
実行ステータスは 1 となりエラー扱いですが、 変数 $L の値は 222 となり、代入されていることがわかります。
read コマンドは標準入力に改行コードが含まれていなかった場合、実行ステータスはエラーとなるが変数への代入は行われます。
while readループすると最終行が読み込まれない場合への対処方法
それでは対処方法について。
「(1)read の実行が失敗した場合」だけでなく、「(2)readした文字列を代入した変数が空の場合」を終了条件とするように処理を追加します。
| |
一番最初のループとの違いは、 || [[ -n "${LINE}" ]] という「OR条件」が追加されている点です。
ひとこと
はじめに考えた人はよく考えたなぁ。