試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

bash,dash,zshのreadコマンドと「while read line」のような行ごとの読み込み処理について

bash,dash,zshの全てのシェルで利用可能なreadコマンドと「while read ...」のループについてを扱う。
本記事内の使用例は3つのシェルの内で最も低機能かつ高速なdashにおいて動作確認済みで、いずれのシェルにおいても動作する。

  1. readコマンド
    1. キーボードからの入力
    2. ファイルからの1行入力
  2. readコマンドとwhile文の組み合わせ(while read ...; do ...; done)
    1. ファイルの内容を1行ずつ処理
    2. 別のコマンドの出力を1行ずつ処理

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==」という指定で実行している。

ファイルからの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

別のコマンドの出力を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

関連URL:

使用したバージョン: