bash,dash,zshのreadコマンドと行ごとの読み込み処理(while read)

Last modified: 2021-09-29

bash,dash,zshの全てのシェルで利用可能なreadコマンドとwhile read ...のループについてを扱う。

本記事内の使用例は3つのシェルの内で最も低機能かつ高速なdashにおいて動作確認済みで、いずれのシェルにおいても動作する。

readコマンド

シェル組み込みコマンドreadは標準入力から入力を受け付けて結果を引数の変数(変数名で指定)に代入する。

キーボードからの入力

readをそのまま用いるとキーボードからの入力を待ち、Enterを押すと入力終了とみなして続きの処理を行う。

(キーボードからの入力を変数ANSに代入)
$ echo "答えを入力してください"; read ANS; echo "入力した答えは:" ${ANS}; unset ANS
答えを入力してください
(ここでキーボードで文字列を入力してEnter)
入力した答えは: [入力した文字列が表示される]

シェルで認識される区切り文字として解釈される変数IFSによりデータを区切って解析し、複数の変数に入れることもできる。

入力する文字列がスペースで区切られているものとして処理する場合はIFSの指定は不要。

$ echo "「名前=値」形式で入力してください"; IFS="=" read NAME VALUE; echo "名前:" ${NAME} "値:" ${VALUE}
「名前=値」形式で入力してください
(ここでキーボードで「[名前]=[値]」の書式で文字列を入力してEnter)
名前: [名前部分が表示される] 値: [値部分が表示される]

上の例では区切りを=にしたのでIFSの値を=にするようにIFS="="(IFS==でも可)という指定で実行している。

ファイルからの1行入力

下のようにすると指定したファイルから1行のデータを読み込んで変数に内容を代入する。

read [変数名] < [ファイル]

readコマンドとwhile文の組み合わせ(while read …; do …; done)

ファイルの内容を1行ずつ処理

下のような構文により、ファイルの内容を1行ずつ読み込んでループし、ループの中で変数の値として処理中の行の内容が得られる。

(ファイルを読み込んで各行を変数に読み込んでループ処理)
while read [変数名]; do [処理...] ; done < [入力ファイル]

下は使用例。

(そのまま表示)
$ while read LINE; do echo ${LINE}; done < [表示したいファイル]; unset LINE

(行番号を付けて表示)
$ I=1; while read LINE; do echo "$(printf %4d ${I})| ${LINE}"; I=$((${I} + 1)); done < [表示したいファイル]; unset I LINE

(5行ごとに行番号を付けて表示)
$ I=1; while read LINE; do [ $((${I} % 5)) -eq 0 ] && echo "$(printf %4d ${I})| ${LINE}" || echo "    | ${LINE}"; I=$((${I} + 1)); done < [表示したいファイル]; unset I LINE

行番号部分の表示の細かい部分などは好みで調整する。また、上の例では10000行以上になると右側の表示がずれるので、これも必要に応じて調整する。

別のコマンドの出力を1行ずつ処理

別のコマンドの標準出力をパイプを用いて受け取り、それをループで処理することもできる。

(コマンド出力の各行を変数に読み込んでループ処理)
[コマンド行] | while read [変数名]; do [処理...] ; done

下は使用例。

(そのまま表示)
$ dmesg | while read LINE; do echo ${LINE}; done; unset LINE

(4桁までの行番号を付けて表示)
$ I=1; dmesg | while read LINE; do echo "$(printf %4d ${I})| ${LINE}"; I=$(( ${I} + 1)); done; unset I LINE

(5行ごとに4桁までの行番号を付けて表示)
$ I=1; dmesg | while read LINE; do [ $((${I} % 5)) -eq 0 ] && echo "$(printf %4d ${I})| ${LINE}" || echo "    | ${LINE}"; I=$((${I} + 1)); done; unset I LINE