Bash
tips
snippet

BASHのwhile readで最終行が処理されない問題の解決方法

More than 1 year has passed since last update.

BASHでリストファイルなどをwhile readすることはよくあると思う。
そんな時のハマりどころ、 『ファイル最終行を処理する方法』 について、すぐ忘れるのでスニペットとしてメモしておく。

問題

BASHで巨大なファイルを読むときなど、こんなコードがメジャーだと思う。

よくあるコード
while read LINE; do
  echo "LINE: ${LINE}"
done < /tmp/test.txt

このコードだと、 最終行に改行文字が付いていないと、最終行が処理されない。
readは改行文字を期待しているからだ。

解決

while read LINEの返戻値がfalseのときでも、空行じゃない場合は処理するようなOR文を入れると良い。

うまく動くコード
while read LINE || [ -n "${LINE}" ]; do
  echo "LINE: ${LINE}"
done < /tmp/test.txt

こういう書き方のほうが読みやすいかもしれない。

うまく動くコードその2
cat /tmp/test.txt | while read LINE || [ -n "${LINE}" ]; do
  echo "LINE: ${LINE}"
done

補足: 試験用のテキストデータ作成

「最終行に改行がないファイル」は、IDEや高機能エディタを使っていると発生しやすい。

しかしviだとうまく再現できないということがわかった。
どうやら最終行に改行を入れてくれるviのお節介仕様のようだ。

ダメな例
$ vi /tmp/vi_test.txt

1
2
3 ⇐ここを改行せずに保存

### 確認 - 最終行が改行してしまっている
$ cat /tmp/vi_test.txt
1
2
3
$   ⇐改行されてプロンプトが出る

:set binary noeol等をすれば改行文字を付けずに保存できるらしいけど・・・絶対忘れる。

ということでviについては深く追わず、手っ取り早く再現する方法を取ることにした。
echo -n "xxxx"あるいはecho -e "xxxx\\c"で、改行しない行を追記する方法だ。

再現方法
$ echo -n "4" >> /tmp/vi_test.txt

### 確認 - 追加行は改行していない
$ cat /tmp/vi_test.txt
1
2
3
4$  ⇐最終行にプロンプトが出る

### 上記の代わりにこれでも良い
$ echo -e "4\\c" >> /tmp/vi_test.txt

この応用でテスト用テキストデータを作れば良い。