本文へスキップ
BecomeCoder

シェルスクリプトコース · 第5章 繰り返し · レッスン23

1行ずつ処理する ― while read の型

ブラウザで完結

導入

「サーバー一覧のファイルを読んで、1台ずつ確認する」「CSVを1行ずつ処理する」。実務のシェルスクリプトで最も出番が多い形が、この while read です。1つの型として、まるごと覚えてしまいましょう。

説明

型はこれです。

#!/bin/bash

while read line; do
  echo "サーバー: $line"
done < servers.txt

ポイントは最後の done < ファイル名。「このループへの入力は、このファイル」という指定です。read が1行ずつ取り出し、読む行が尽きると終了ステータスが 0 以外になって、ループが自然に終わります。

flowchart TD
    A["servers.txt"] --> B["while read line"]
    B --> C{"読めた?"}
    C -->|"読めた"| D["line に1行入る → 中身を実行"]
    D --> B
    C -->|"もう無い"| E["done(終了)"]

read は複数の変数分けて受け取ることもできます。空白区切りの列を、名前付きで扱えます。

while read ip method path status; do
  echo "$status : $path"
done < logs/access.log

logs/access.log は「IP メソッド パス ステータス」の並びなので、4つの変数に自然に分かれます。cut -d ' ' -f 4 と書くより、ずっと意図が読めるコードになります。

なぜ cat ファイル | while read line ではなく done < ファイル なのか。パイプで渡すとループが別のプロセスで動き、ループ内で変えた変数がループの外に残らないという落とし穴があるからです。done < ファイル なら、その心配がありません。

ファイル1行ずつの処理は for line in $(cat file) でも書けそうに見えますが、やってはいけません。行ではなく空白で区切られてしまい、スペースを含む行が壊れます。1行ずつ読むなら while read。これは定石です。

やってみよう

スクリプトを実行して、servers.txt の3行が1つずつ処理されることを確かめましょう。次に done < servers.txtdone < logs/access.log に変え、read lineread ip method path status にして、echo "$status : $path" を出してみてください。列に名前がつくと読みやすさが変わります。

演習

logs/access.logwhile read で1行ずつ読み、各行のステータス(4列目)とパス(3列目)を ステータス: 403 のような形で出してください(403 が含まれていればOK)。

ヒント1を見る

while read ip method path status; dodone < logs/access.log

ヒント2を見る

ループの中は echo "ステータス: $status"

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

シェルスクリプト(シミュレート)

スクリプトを書くエディタと、それを動かす仮想シェルを読み込みます(本物のbashではなく、動きを再現した学習用の環境です)。
スクロールして表示された時点でも自動で読み込まれます。