while read line について

while read line は、シェルスクリプトでコマンドの実行結果を 1行ずつ読込みながら処理したいときによく使います。

内容は短いですが、実際によく使われているので、パターン化していつでも参考にして状況に応じて使えるようにまとめてみました。

  • Here Document 内に記述した値を使う場合
  • コマンドを実行すると同時に使う場合
  • 変数に格納した値を Here Document で読込んで使う場合
  • 標準入力リダイレクトでファイルを読込んで使う場合
  • オマケ : 複数の Here Document を使う場合
  • その他、注意点

Here Document 内に直接記述した値を使う場合

Here Document を使って、ENDEND の間に直接記述した内容を 1行ずつ読込みます。

read_line_test.shRaw Code(S)Raw Code(T)
#!/bin/bash

cnt=0
while read line
do
    cnt=`expr $cnt + 1`
    echo "LINE $cnt : $line"
done <<END
ABC DEF GHI JKL MNO PQR STU VWX YZA
BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
CDE FGH IJK LMN OPQ RST UVW XYZ ABC
END
$ ./read_line_test.sh
LINE 1 : ABC DEF GHI JKL MNO PQR STU VWX YZA
LINE 2 : BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
LINE 3 : CDE FGH IJK LMN OPQ RST UVW XYZ ABC

コマンドを実行すると同時に使う場合

コマンドを実行すると同時にその結果を 1行ずつ読込みます。

$ cat test.txt
ABC DEF GHI JKL MNO PQR STU VWX YZA
BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
CDE FGH IJK LMN OPQ RST UVW XYZ ABC
read_line_test.shRaw Code(S)Raw Code(T)
#!/bin/bash

cnt=0
cat /tmp/test.txt | while read line
do
    cnt=`expr $cnt + 1`
    echo "LINE $cnt : $line"
done
$ ./read_line_test.sh
LINE 1 : ABC DEF GHI JKL MNO PQR STU VWX YZA
LINE 2 : BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
LINE 3 : CDE FGH IJK LMN OPQ RST UVW XYZ ABC

変数に格納した値を Here Document で読込んで使う場合

変数にコマンドの実行結果を格納し、変数の中身を Here Document を使って1行ずつ読込みます。

デバッグする際にも、変数に値が入ってたほうが、いろいろ便利なので、私は、このパターンを好んで使っています。

Here Document を使って、ENDEND の間に値が入っている変数を置きます。

$ cat test.txt
ABC DEF GHI JKL MNO PQR STU VWX YZA
BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
CDE FGH IJK LMN OPQ RST UVW XYZ ABC
read_line_test.shRaw Code(S)Raw Code(T)
#!/bin/bash

DATA=`cat /tmp/test.txt`

cnt=0
while read line
do
    cnt=`expr $cnt + 1`
    echo "LINE $cnt : $line"
done <<END
$DATA
END
$ ./read_line_test.sh
LINE 1 : ABC DEF GHI JKL MNO PQR STU VWX YZA
LINE 2 : BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
LINE 3 : CDE FGH IJK LMN OPQ RST UVW XYZ ABC

標準入力リダイレクトでファイルを読込んで使う場合

最後に、標準入力リダイレクトでファイルを読込みながら 1行ずつ処理します。

私は、特に XML ファイルを解析する際によく使っています。

以下の例では、/tmp/test.txt ファイルの中身を読込んでいます。

$ cat test.txt
ABC DEF GHI JKL MNO PQR STU VWX YZA
BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
CDE FGH IJK LMN OPQ RST UVW XYZ ABC
read_line_test.shRaw Code(S)Raw Code(T)
#!/bin/bash

cnt=0
while read line
do
    cnt=`expr $cnt + 1`
    echo "LINE $cnt : $line"
done < /tmp/test.txt
$ ./read_line_test.sh
LINE 1 : ABC DEF GHI JKL MNO PQR STU VWX YZA
LINE 2 : BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
LINE 3 : CDE FGH IJK LMN OPQ RST UVW XYZ ABC

オマケ : 複数の Here Document を使う場合

状況によっては、一つのスクリプト内で複数の Here Document を使いたい場合もあるかと思います。

当記事内で Here Document を使う際に、END ~ END のような書き方をしていましたが、実際に END というのは、決まり文句ではありません。

基本的には END ~ END を使い回しても大丈夫ですが、他の文字列、例えば、END の代わりに、OH-MY-GOT でも、特殊文字である !!! でも良いです。

しかし、特殊文字に関しては、コメントアウトを表す #、プロセス番号を表す $$、バックグラウンド処理を表す & など、シェルによって勝手に解釈される可能性があるため(エラーで動かない)、使わないほうが良いです。

よって、Here Document の開始・終了文を書く際には、なるべく文字列 (アルファベット) を使ってください

$ cat test.txt
ABC DEF GHI JKL MNO PQR STU VWX YZA
BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
CDE FGH IJK LMN OPQ RST UVW XYZ ABC
read_line_test.shRaw Code(S)Raw Code(T)
#!/bin/bash

DATA=`cat /tmp/test.txt`

echo "----------- OH-MY-GOT -----------"

cnt=0
while read line
do
    cnt=`expr $cnt + 1`
    echo "LINE $cnt : $line"
done <<OH-MY-GOT
$DATA
OH-MY-GOT


echo
echo "----------- !!! -----------"

cnt=0
while read line
do
    cnt=`expr $cnt + 1`
    echo "LINE $cnt : $line"
done <<!!!
$DATA
!!!


echo
echo "----------- %%% -----------"

cnt=0
while read line
do
    cnt=`expr $cnt + 1`
    echo "LINE $cnt : $line"
done <<%%%
$DATA
%%%
$ ./read_line_test.sh
----------- OH-MY-GOT -----------
LINE 1 : ABC DEF GHI JKL MNO PQR STU VWX YZA
LINE 2 : BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
LINE 3 : CDE FGH IJK LMN OPQ RST UVW XYZ ABC

----------- !!! -----------
LINE 1 : ABC DEF GHI JKL MNO PQR STU VWX YZA
LINE 2 : BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
LINE 3 : CDE FGH IJK LMN OPQ RST UVW XYZ ABC

----------- %%% -----------
LINE 1 : ABC DEF GHI JKL MNO PQR STU VWX YZA
LINE 2 : BCD EFG HIJ KLM NOP QRS TUV WXY ZAB
LINE 3 : CDE FGH IJK LMN OPQ RST UVW XYZ ABC

終わりに

Here Document を使う時に一つ注意点がありまして、例えば、ENDEND の間に置かれる値終了文字列 END の前に見た目を綺麗にするために、スペースを 4つあけるような処理は入れないでください(空白を入れない)。 誤動作の原因になります。

例えば、if、while、for、case のような制御文内で、Here Document を使う際にも、以下のように。

制御文内での Here DocumentRaw Code(S)Raw Code(T)
#!/bin/bash

if [ 100 -gt 50 ]; then

    DATA=`cat /tmp/test.txt`

    cnt=0
    while read line
    do
        cnt=`expr $cnt + 1`
        echo "LINE $cnt : $line"
    done <<END
$DATA
END

fi

以上で、シェルスクリプトでよく使われる while read line 4パターンでした。