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