インデントされたファイルを`while read`コマンドでループさせるとインデントが消えてしまう問題に対処

Bash, Linux

はじめに

シェルスクリプトで、インデントされたテキストファイルを while read 構文で読み込み処理したいが、インデントされている行の行頭のスペースが消えてしまうのでなんとかしたいという質問がありました。

検証環境

$ uname -moi
x86_64 x86_64 GNU/Linux

$ bash -version | head -n 1
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

問題

以下のようなファイルがあります。2行目がスペースでインデントされています。

$ cat a.txt
111
    222
333 444

これを while read 構文で読み込みループさせたいが、2行目の行頭のスペースが消えてしまうということでした。

$ while read -r line; do
    echo "$line"
  done <a.txt

111
222
333 444

解決方法は IFS 環境変数のクリア

解決方法は非常に単純で、 IFS 環境変数をクリア(空)にしてやることで対処できます。

$ while IFS= read -r line; do
    echo "$line"
  done <a.txt

111
    222
333 444

IFS 環境変数はinternal fieled separatorの省略で、 情報を分離するために利用される文字 が格納されています。
read コマンドはシェルのビルドインコマンドです。

$ type read
read is a shell builtin

標準入力で受け取った情報を IFS 環境変数に設定されている文字列で自動的に分割した後、引数で与えられた変数名(ここではline)に格納します。

IFS= read とすることで、一時的に区切り文字列を無視するようにしています。

IFS 環境変数を覗いてみる

では IFS 環境変数とはどのような値なのでしょうか?

頑張って覗けるだけ覗いてみます。

普通に echo してみます。(ここでは -n オプションを指定して、echo コマンド自身が最後出力する改行文字を抑制しています。)

$ echo -n "$IFS"

なにか出力されていそうですがわかりませんね。

文字列を [...] で囲んでみます。

$ echo -n "[$IFS]"
[
]

改行文字を含んでいるようです。

文字数がいくつか確認してみます。

# wc で文字数を確認
$ echo -n "$IFS" | wc -c
3

# @に置換してみる
$ echo -n "$IFS" | sed -z 's/./@/g'
@@@

どうやら3文字ですね。

ファイルに出力してみます。

$ echo -n "$IFS" > hoge

$ ls -l hoge
-rw-r--r-- 1 root root 3 Feb  7 11:07 hoge

3byte。

vi で開くと更にわかりますが、これはスペース、タブ、改行の3つから構成されています。

ひとこと

while read でのインデント消える問題の解消から IFS 環境変数の話へと発展してしまいました。

Bash, Linux