そんなつまずきを抱えたまま、forループやcatを組み合わせて試行錯誤していませんか。
実は、Bashには
while read というファイルを1行ずつ読み込むための専用の書き方があります。ログファイルの解析、CSVの一括処理、サーバー一覧への順次SSH接続など、現場で繰り返し使う場面がある実務必須の構文です。
この記事では、
while read の基本構文から、IFSによる区切り文字のカスタマイズ、CSVやTSVの処理、よくあるトラブルの対処法まで、RHEL 9.4 / Ubuntu 24.04 LTS の動作確認済みコマンドで解説します。
この記事のポイント
・ while read line でファイルを1行ずつ確実に処理できる
・ IFS=","で区切り文字を変えればCSV・TSVにも対応できる
・ while readはfor+catより安全で空白・改行を壊さない
・ 末尾改行なし・バックスラッシュ・末尾スペースに注意が必要
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
while readとは何か|forループと何が違うのか
シェルスクリプトでファイルを処理するとき、最初に思いつくのがfor ループです。しかし、
for line in $(cat file) という書き方には重大な落とし穴があります。forループの3つの問題点:
・ スペースや改行を含む行が途中で分割される(IFSの影響)
・ ワイルドカード(*)がファイル名展開される場合がある
・ 大きなファイルをcat(コマンド置換)で一括読み込みするためメモリを消費する
一方、
while read は標準入力を1行ずつ読み込むため、これらの問題が発生しません。現場のシェルスクリプトでは
while read が圧倒的に多用されています。forループとwhile readの比較:
| 比較項目 | for line in $(cat file) | while read -r line |
|---|---|---|
| スペース含む行 | 分割されてしまう | 正しく1行として処理できる |
| ワイルドカード展開 | 展開される(危険) | 展開されない(安全) |
| 大きなファイル | 一括読込でメモリを消費する | 1行ずつストリーム処理 |
| バックスラッシュ | 展開されてしまう | read -r付きで保持できる |
| 推奨度 | 非推奨 | 推奨(現場の標準) |
readコマンドの主要オプション一覧
while read で使える read コマンドの主要オプションをまとめました。| オプション | 説明 | 使用例 |
|---|---|---|
| -r(バックスラッシュ無効化) | バックスラッシュをエスケープとして解釈しない | while read -r line; do |
| -d(区切り文字変更) | 行末の区切り文字を変更する | while read -r -d '' line; do |
| -n(読込文字数制限) | 指定した文字数だけ読み込む | read -r -n 1 char |
| -t(タイムアウト) | タイムアウトを設定する | read -r -t 5 line |
| -s(非表示入力) | 入力を表示しない(パスワード入力等) | read -r -s password |
| -a(配列代入) | 入力を配列に代入する | read -r -a myarray |
while readの基本構文
1. ファイルを1行ずつ読み込む
最もシンプルな書き方です。# 基本構文 while read line; do echo "$line" done < /path/to/file.txt
# テスト用ファイルを作成する $ cat /tmp/servers.txt web01.example.com web02.example.com db01.example.com # while readで1行ずつ処理する $ while read line; do > echo "接続先: $line" > done < /tmp/servers.txt 接続先: web01.example.com 接続先: web02.example.com 接続先: db01.example.com
< ファイル名 です。これはリダイレクト(標準入力の切り替え)で、ファイルの内容をwhileループの入力として渡しています。
2. readのオプション|バックスラッシュ展開を防ぐ -r
read にはよく一緒に使われる重要なオプションがあります。# -r オプション: バックスラッシュを文字として扱う(展開しない) while read -r line; do echo "$line" done < file.txt
-r を付けない場合、ファイル内の \n や \t がエスケープシーケンスとして解釈されることがあります。パスやログ解析など実務では 基本的に
-r を付ける ことを習慣にしてください。3. 変数を複数指定して列ごとに取り出す
read は複数の変数を指定でき、空白で区切られた値を列ごとに受け取れます。# テスト用ファイル(ユーザー名とIPアドレスの対応表) $ cat /tmp/user-ip.txt alice 192.168.1.10 bob 192.168.1.20 carol 192.168.1.30 # 変数を2つ指定して列ごとに受け取る $ while read -r username ipaddr; do > echo "ユーザー: $username, IP: $ipaddr" > done < /tmp/user-ip.txt ユーザー: alice, IP: 192.168.1.10 ユーザー: bob, IP: 192.168.1.20 ユーザー: carol, IP: 192.168.1.30
これを意図的に利用して最初の1列だけ取り出し、残りは捨てる書き方もよく使われます。
IFSで区切り文字を変えてCSV・TSVを処理する
1. IFSとは何か
IFS(Internal Field Separator)はシェルが単語の区切りとして使う文字を定義する変数です。デフォルトは「スペース・タブ・改行」ですが、
while read の前に変更することでCSVやTSVなど任意の区切り文字に対応できます。
2. CSVファイルを処理する
# CSVファイルの例 $ cat /tmp/users.csv alice,30,東京 bob,25,大阪 carol,35,名古屋 # IFS="," を設定してCSVを処理する $ while IFS="," read -r name age city; do > echo "名前:$name 年齢:$age 都市:$city" > done < /tmp/users.csv 名前:alice 年齢:30 都市:東京 名前:bob 年齢:25 都市:大阪 名前:carol 年齢:35 都市:名古屋
while IFS="," read -r ... のように while read の直前で設定するのが定石です。この書き方にすることでIFSの変更がループ内にだけ影響し、スクリプト全体への副作用を防げます。
3. TSVファイル(タブ区切り)を処理する
# TSVファイルを処理する(IFSにタブを指定) while IFS=$'\t' read -r col1 col2 col3; do echo "$col1 / $col2 / $col3" done < /tmp/data.tsv
\t と書けますが、$'\t' の形式(ANSI-C Quoting)を使うと確実です。実務で使えるwhile readの応用パターン
1. サーバー一覧に順次SSHで接続してコマンドを実行する
現場でよく使うパターンです。サーバーリストを読み込んで1台ずつ操作します。#!/bin/bash # server-check.sh: サーバー一覧に接続してdiskを確認する SERVERS="/etc/server-list.txt" while read -r server; do echo "=== $server ===" ssh -n -o ConnectTimeout=5 "$server" "df -h /" done < "$SERVERS"
ConnectTimeout=5 で接続タイムアウトを設定することで、応答しないサーバーで止まらないようにしています。-n オプションはSSHが標準入力を奪わないようにするための設定で、while readと組み合わせる場合は必須です。2. ログファイルから特定パターンの行を抽出して集計する
#!/bin/bash # log-count.sh: /var/log/secure からSSH失敗ログをIPごとに集計する declare -A fail_count while read -r line; do if echo "$line" | grep -q "Failed password"; then # IPアドレスを抽出する ip=$(echo "$line" | awk '{print $NF}') ((fail_count["$ip"]++)) fi done < /var/log/secure # 集計結果を表示する echo "--- SSH失敗 IPアドレス集計 ---" for ip in "${!fail_count[@]}"; do echo "${fail_count[$ip]}回 $ip" done | sort -rn
--- SSH失敗 IPアドレス集計 --- 47回 203.0.113.45 23回 198.51.100.12 8回 192.0.2.88
declare -A はBashの連想配列(ハッシュ)です。IPアドレスをキーにして失敗回数をカウントしています。このパターンはfail2banのルール調整前の「どのIPが多いか」確認に実際に使っています。
3. CSVからユーザーを一括作成する
#!/bin/bash # create-users.sh: CSVからユーザーを一括作成する # フォーマット: username,uid,group,comment while IFS="," read -r username uid group comment; do # ヘッダー行をスキップする [[ "$username" == "username" ]] && continue useradd -u "$uid" -g "$group" -c "$comment" "$username" echo "作成完了: $username (uid=$uid)" done < /tmp/new-users.csv
[[ ... ]] && continue でヘッダー行をスキップしています。CSVの1行目がカラム名の場合に有効な定型パターンです。
4. パイプとプロセス置換でコマンド出力を処理する
ファイルだけでなく、コマンドの出力をwhile read に渡すことも可能です。# パイプ接続(変数がサブシェル内で消える問題あり) ps aux | grep httpd | grep -v grep | while read -r user pid cpu mem rest; do echo "PID:$pid CPU:$cpu% MEM:$mem% USER:$user" done # プロセス置換を使う書き方(サブシェル問題を回避する) while read -r user pid cpu mem rest; do echo "PID:$pid CPU:$cpu% MEM:$mem% USER:$user" done < <(ps aux | grep httpd | grep -v grep)
|)でwhile readに接続すると、ループ内の変数代入がサブシェルで実行されるためループ終了後に変数の値が失われます。これを避けるには
<<(...) のプロセス置換を使います。トラブルシュート|よくある問題と対処法
1. 「末尾の行が処理されない」問題
ファイルの最終行に改行がない場合、while read がその行を読み飛ばすことがあります。# 末尾改行なしのファイルでも確実に処理する書き方 while read -r line || [[ -n "$line" ]]; do echo "$line" done < file.txt
|| [[ -n "$line" ]] を追加することで、readがEOF(ファイル終端)で失敗したときでも変数
$line に値が残っていれば処理を続けます。本番スクリプトでは常にこの書き方を推奨します。2. 「行頭・行末のスペースが消える」問題
デフォルトのIFSにスペースが含まれているため、行頭と行末のスペースがトリムされます。# IFSを空にして行頭・行末のスペースを保持する while IFS= read -r line; do echo "$line" done < file.txt
IFS=(空文字)にすることでスペースのトリムを防ぎ、行の内容を厳密に保持できます。インデントを含む設定ファイルの処理などで有効です。
3. 「ループ内のSSHが1回で止まる」問題
sshなどの対話コマンドがwhile readの標準入力を奪ってしまい、ループが1回で終わることがあります。# 解決策1: sshに -n オプションを付けてstdinを/dev/nullにする while read -r server; do ssh -n "$server" "uptime" done < servers.txt # 解決策2: ファイルディスクリプタ3番を使ってstdinと分離する exec 3< servers.txt while read -r server <&3; do ssh "$server" "uptime" done exec 3<&-
ssh -n は /dev/null からstdinを読み込む指定です。ファイルディスクリプタを使う2つ目の方法はより汎用性が高く、rsyncやscpなど他のコマンドにも対応できます。
4. 「バックスラッシュが消える」問題
# NGな書き方(-rなし) while read line; do echo "$line" done < file.txt # OKな書き方(-r付き) while read -r line; do echo "$line" done < file.txt
-r なしの場合、行末の \ を「継続行の記号」として解釈するため次の行と結合されます。ファイルパスやURLを処理するスクリプトでは特に問題が起きやすいため、
-r は必ず付けてください。本番スクリプト安全化チェックリスト
本番環境のシェルスクリプトでwhile readを使う前に、以下の項目を確認してください。・ -r オプション: 付いているか(バックスラッシュの意図しない展開を防ぐ)
・ IFSの設定: デフォルトのままでいいか(スペース保持が必要なら
IFS= にする)・ 末尾改行対策:
|| [[ -n "$line" ]] を付けているか・ SSH・対話コマンド:
-n オプションまたはFD分離をしているか・ 変数のクォート:
"$line" のようにダブルクォートで囲んでいるか(スペース含む値の分割防止)・ ファイル存在チェック: ループ前に
[ -f "$FILE" ] || exit 1 を書いているかよくある質問(FAQ)
Q. forループとwhile readはどちらを使うべきですか?A. ファイルを1行ずつ処理する場合は
while read を選んでください。スペースや改行を含む行を安全に扱えます。for は配列の要素や数値のループに向いています。Q. while readは大きなファイルでも使えますか?
A. はい、使えます。
while read は1行ずつストリーム処理するため、メモリ使用量は行の長さに比例するだけでファイルサイズに依存しません。数十GBのログファイルにも問題なく使用できます。
Q. while readで読んだ変数がループ外で使えません。
A. パイプ(
|)でwhile readに接続している場合、ループがサブシェルで実行されるため変数がループ外に出ません。< file によるリダイレクト、または <<(command) のプロセス置換に変更してください。Q. while readでCSVの中にカンマが含まれるフィールドを処理できますか?
A.
IFS="," だけでは "field,with,comma" のような引用符付きフィールドを正しく処理できません。Pythonやawkなど、RFC 4180準拠のCSVパーサーを使うことを推奨します。
本記事のまとめ
Linux ポート確認の全コマンドでも紹介しているように、シェルスクリプトはLinux運用の中心にあります。while read を使いこなせると、ポート確認・ログ解析・設定変更など幅広い作業を自動化できます。また、ls コマンドの基本オプションと組み合わせることで、ディレクトリ内のファイルをリスト化してwhile readで処理するパターンも作れます。
| やりたいこと | コマンド・書き方 |
|---|---|
| ファイルを1行ずつ処理する | while read -r line; do ... done < file.txt |
| CSVを列ごとに処理する | while IFS="," read -r col1 col2; do ... done < file.csv |
| スペースを保持して処理する | while IFS= read -r line; do ... done < file.txt |
| 末尾改行なしファイルを処理する | while read -r line || [[ -n "$line" ]]; do ... done |
| コマンド出力をループで処理する | while read -r line; do ... done < <(command) |
| SSHをwhileで使う(stdin分離) | while read -r server; do ssh -n "$server" "cmd"; done < servers.txt |
シェルスクリプトを書いても、毎回ネット検索に頼っていませんか?
while readひとつとっても、IFSの落とし穴やSSHとの組み合わせを知らなければ、本番環境での障害対応で判断を誤ります。
ネットの切れ端の情報をコピペするだけでなく、現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 次のページへ:ps2pdfコマンドでPostScriptをPDFに変換する方法|LinuxでのPDF変換の実践例
- 前のページへ:ip routeコマンドでLinuxのルーティングテーブルを確認・設定する方法|静的ルート追加とトラブルシュートも
- この記事の属するカテゴリ:Linuxtips・シェルスクリプトへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます