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
この応用でテスト用テキストデータを作れば良い。