シェルスクリプト: テキストファイルを 1 行ずつ読み込む (read)

テキストファイルを 1 行だけ読み込む

read コマンドは、ユーザからの入力を 1 行取得するために使用できますが、ファイルからの入力を 1 行読み込むのにも使用できます。

sample.sh (input.txt の内容を 1 行だけ読み込む)
#!/bin/bash

read line < input.txt
echo "$line"
input.txt(入力データ)
AAA   100
BBB   200
CCC   300
実行結果
$ ./sample.sh
AAA   100

これだけだとあまり役に立たないので、通常は次のように while ループを組み合わせて、すべての行を読み出します。

テキストファイルから 1 行ずつ読み込む

read コマンドと while ループを組み合わせると、テキストファイルのすべての行を 1 行ずつ処理できます。

sample.sh (input.txt の内容を 1 行ずつ読み込む)
#!/bin/bash

while read line; do
  echo "$line"
done < input.txt
input.txt(入力データ)
AAA   100
BBB   200
CCC   300
実行結果
$ ./sample.sh
AAA   100
BBB   200
CCC   300
☝️ ワンポイント echo のパラメータ "$line" をダブルクォートで囲んでいることに注意してください。 こうすることで、1 行分の文字列を、ひとつのパラメータとして渡すことができます。 ダブルクォートで囲まないと、echo AAA 100 のように、2 つのパラメータが渡されたかのように処理されてしまうため、連続するスペースが 1 つのスペースに置き換えられて出力されてしまいます。

行頭のスペースが消えてしまうのを防ぐ

例えば、下記のように行頭がインデントされたテキストファイルを読み込むとします。

input.txt(入力データ)
・カテゴリA
    ・カテゴリA-1
    ・カテゴリA-2
・カテゴリB
    ・カテゴリB-1
    ・カテゴリB-2

このファイルを次のように読み込むと、行頭のスペースが削除されてしまします。

sample.sh
#!/bin/bash

while read line; do
  echo "$line"
done < input.txt
実行結果
$ ./sample.sh
・カテゴリA
・カテゴリA-1
・カテゴリA-2
・カテゴリB
・カテゴリB-1
・カテゴリB-2

行頭のスペースをそのまま表示したい場合は、次の例のように、組み込み変数 IFS を空文字にセットしてから処理するようにします。 変更した IFS の値は、最後に行儀よく元の値に戻してあげてください(source コマンドなどでスクリプトを読み込んだときに、呼び出し元シェルの IFS の値が変更されたままになってしまうのを防ぐため)。

sample.sh
#!/bin/bash

old_ifs=$IFS
IFS=''

while read line; do
  echo "$line"
done < input.txt

IFS=$old_ifs
実行結果
$ ./sample.sh
・カテゴリA
    ・カテゴリA-1
    ・カテゴリA-2
・カテゴリB
    ・カテゴリB-1
    ・カテゴリB-2

IFS 特殊変数に関しての詳細は、man bash を参照してください。 IFS は内部的な文字列のセパレート処理に使われるキャラクター群を保持しています。