read コマンドは、標準入力から 1行の入力を読み取り、変数に格納 するためのシェル組み込みコマンドです。
対話的にユーザー入力を受け付けたり、スクリプト内で入力値を処理する際に利用されます。
構文(Syntax)
read [オプション] [変数...]
主なオプション一覧
| オプション | 説明 | 使用例 |
|---|---|---|
| (なし) | 入力を1行読み取り、指定した変数に格納 | read name |
-p "PROMPT" | 入力前にメッセージを表示 | read -p "Enter your name: " name |
-s | 入力を非表示(パスワード入力向け) | read -s password |
-t N | 入力を待つ秒数を指定(タイムアウト) | read -t 5 name |
-n N | N文字だけ読み取る | read -n 1 key |
-r | バックスラッシュをエスケープ文字として扱わない | read -r line |
-a ARRAY | 入力を配列に格納 | read -a items |
-d DELIM | 区切り文字を指定(デフォルトは改行) | read -d : input |
実行例
基本的な入力の読み取り
read name
echo "Hello, $name"
実行例:
Alice ← ユーザー入力
Hello, Alice
プロンプト付きで入力
read -p "Enter your name: " name
echo "Welcome, $name"
パスワードを非表示で入力
read -s -p "Password: " password
echo
echo "Password entered (hidden)"
5秒以内に入力がなければタイムアウト
read -t 5 -p "Enter your name: " name
1文字だけ入力を取得
read -n 1 -p "Press any key: " key
echo "You pressed '$key'"
改行やエスケープをそのまま読み取る
read -r line
配列として入力を格納
read -a items
echo "First item: ${items[0]}"
入力例:
apple banana cherry
出力例:
First item: apple
区切り文字(:)までを入力として取得
echo "abc:def:ghi" | read -d : var
echo "$var"
出力例:
abc
エラー例(タイムアウト)
read -t 2 name
出力例:
(2秒経過後に終了、変数は空)
csvファイルの取り扱い
read コマンドを使うことでcsvファイルに記載された情報による処理の繰り返し実行に対応できます。
1) 基本:カンマ区切り(引用・カンマ埋め込みなし想定)
#!/usr/bin/env bash
# basic_csv_read.sh
# 例: ./basic_csv_read.sh sample.csv
set -o errexit -o nounset -o pipefail
csv_file="${1:-sample.csv}"
# ヘッダーを読み飛ばしたいときは tail -n +2 を使う
tail -n +2 "$csv_file" | while IFS=, read -r id name age; do
# 空行スキップ
[[ -z "${id}${name}${age}" ]] && continue
printf 'id=%s, name=%s, age=%s\n' "$id" "$name" "$age"
done
- 前提:
"Alice, Bob"のようなフィールド内カンマや二重引用符が無いCSV向け。
2) 可変列数を配列で受ける(列数がバラつくCSV向け)
#!/usr/bin/env bash
# csv_to_array.sh
# 例: ./csv_to_array.sh sample.csv
set -o errexit -o nounset -o pipefail
csv_file="${1:-sample.csv}"
# ヘッダー飛ばし
{ read -r _header; cat; } < "$csv_file" | \
while IFS=, read -r -a cols; do
[[ ${#cols[@]} -eq 0 ]] && continue
echo "列数=${#cols[@]} / 先頭=${cols[0]}"
# 例: 3列目を安全に参照
if (( ${#cols[@]} >= 3 )); then
echo "3列目=${cols[2]}"
fi
done
read -aで配列に展開できます(ただし引用・埋め込みカンマは未対応)。
3) RFC4180 風:ダブルクォート対応(埋め込みカンマ・二重の二重引用符 “” を扱う)
Bash だけで素朴にパースする小さな関数。改行を含むフィールドは未対応ですが、一般的な
"a, b","x""y"を処理できます。
#!/usr/bin/env bash
# csv_read_rfcish.sh
# 例: ./csv_read_rfcish.sh sample_quoted.csv
set -o errexit -o nounset -o pipefail
csv_read_line() { # usage: csv_read_line array_name ; reads 1 logical line from stdin
local __arr_name=$1
local line
IFS= read -r line || return 1
local -a fields=()
local field="" c next in_quotes=0
local i=0 len=${#line}
while (( i < len )); do
c=${line:i:1}
if (( in_quotes )); then
if [[ $c == '"' ]]; then
next=${line:i+1:1}
if [[ $next == '"' ]]; then
field+='"' # 連続する "" はエスケープされた "
((i++))
else
in_quotes=0 # クォート終了
fi
else
field+="$c"
fi
else
case "$c" in
'"') in_quotes=1 ;; # クォート開始
',') fields+=("$field"); field="" ;;
$'\r') ;; # CR 無視(Windows 改行対策)
*) field+="$c" ;;
esac
fi
((i++))
done
fields+=("$field")
# nameref 経由で呼び出し側の配列に代入(bash 4.3+)
local -n __ref=$__arr_name
__ref=("${fields[@]}")
return 0
}
csv_file="${1:-sample_quoted.csv}"
# ヘッダーを先に1行読む(必要なければ削除)
{
IFS= read -r header || true
echo "HEADER: $header"
while csv_read_line cols; do
# 空行スキップ
[[ ${#cols[@]} -eq 1 && -z ${cols[0]} ]] && continue
printf 'Row:'
for ((i=0; i<${#cols[@]}; i++)); do
printf ' [%d]=<%s>' "$i" "${cols[$i]}"
done
printf '\n'
done
} < "$csv_file"
テスト用 sample_quoted.csv
id,name,comment
1,"Alice","Hello, world"
2,"Bob","He said ""Good luck!"" yesterday"
3,"Carol","no quotes"
4) CSVを書き出す(値を安全にエスケープして出力)
#!/usr/bin/env bash
# csv_write.sh
# 例: ./csv_write.sh > out.csv
set -o errexit -o nounset -o pipefail
csv_escape() {
local s=$1
s=${s//\"/\"\"} # " を "" に
if [[ "$s" == *[,\"$'\n'$'\r']* ]]; then
printf '"%s"' "$s"
else
printf '%s' "$s"
fi
}
# 見出し
printf '%s,%s,%s\n' "id" "name" "note"
# データ例
id=1; name='Alice'; note='Hello, world'
printf '%s,%s,%s\n' \
"$(csv_escape "$id")" \
"$(csv_escape "$name")" \
"$(csv_escape "$note")"
id=2; name='Bob'; note='He said "Good luck!"'
printf '%s,%s,%s\n' \
"$(csv_escape "$id")" \
"$(csv_escape "$name")" \
"$(csv_escape "$note")"
補足(実務Tips)
- 行末
\rが混ざるCSV(Windows由来)は、読み取り時にread -rしつつ上の実装のように\rを無視するか、事前にdos2unix等で正規化すると安定します。 - 完全なCSV互換(改行入りフィールドなど)を厳密にやるなら、
mlr,awk(FPAT),python -c 'import csv'などの専用パーサを併用するのが安全です。
IFSの役割と読み込み時の注意点
read は入力を IFS(Internal Field Separator) で区切って変数に格納します。デフォルトのIFSは半角スペース・タブ・改行です。複数の変数を指定した場合、余った分はすべて最後の変数にまとめて入ります。
echo "a b c d" | { read w1 w2 rest; echo "w1=$w1 / w2=$w2 / rest=$rest"; }
# → w1=a / w2=b / rest=c d
CSVのようにカンマ区切りで読みたい場合は、IFS=, のように一時的にIFSを変更します。
IFS=, read -r col1 col2 col3 <<< "a,b,c"
echo "$col1 / $col2 / $col3"
# → a / b / c
while readループでハマりやすい罠
command | while read line; do ... done のようにパイプで while read に渡すと、ループ内がサブシェルで実行されます。そのため、ループ内で変更した変数はループの外から参照できません。
# NG例:countがループ外で0のまま
count=0
seq 1 3 | while read -r n; do
count=$((count + 1))
done
echo "count=$count"
# → count=0(サブシェル内でしか更新されていない)
# OK例:プロセス置換でサブシェル化を避ける
count=0
while read -r n; do
count=$((count + 1))
done < <(seq 1 3)
echo "count=$count"
# → count=3
詳しくは サブシェルの落とし穴|変数消失の防止 も参照してください。
シェル(bash/sh/zsh/ksh)による read の違い
read は各シェルの組み込みコマンドですが、オプションの対応状況が異なります。sh(dash)向けにスクリプトを書く場合は特に注意してください。
| オプション | bash | dash(sh) | zsh | ksh |
|---|---|---|---|---|
-p(プロンプト) | ○ | × | ○ | ○ |
-s(非表示) | ○ | × | ○ | ○ |
-t(タイムアウト) | ○ | × | ○ | ○ |
-a(配列) | ○ | × | △(-A) | ○ |
-n(文字数指定) | ○ | × | ○ | ○ |
#!/bin/sh で始まるスクリプトが dash で実行される環境(Debian/Ubuntu系)では、-p や -a などのbash拡張オプションはエラーになります。移植性を重視するなら read var の基本形とリダイレクト・パイプの組み合わせに留めるのが安全です。
readarray / mapfile との違い(配列読み込み)
read -a は1行分の入力を空白区切りで配列に格納しますが、複数行をまとめて配列化したい場合は mapfile(readarray)を使います。
mapfile -t lines < sample.txt
echo "行数: ${#lines[@]}"
echo "1行目: ${lines[0]}"
1行ずつ処理しつつ変数に安全に格納したいなら while read、ファイル全体を配列として一括保持したいなら mapfile が適しています。詳しくは mapfile – 標準入力を配列に読み込む を参照してください。
よくある質問(FAQ)
Q. read -r を付けないとどうなる?
A. -r を付けないと、入力中のバックスラッシュ(\)がエスケープ文字として解釈され、改行の継続やバックスラッシュの消失など意図しない挙動になることがあります。ファイルパスなど \ を含む可能性のある入力を扱う場合は、常に read -r を使うのが安全です。
Q. read が入力を待たずにすぐ終了してしまう
A. 標準入力がすでにパイプやリダイレクトで別のデータに置き換わっている、もしくは -t でタイムアウトが短く設定されている可能性があります。対話的に入力させたい場合は、標準入力が端末(/dev/tty)に接続されているか確認してください。
Q. shスクリプトでread -pやread -sが使えない
A. -p/-s/-t/-a などはbash拡張のオプションで、POSIX準拠の sh(dash)では未対応です。#!/bin/sh のスクリプトでは printf でプロンプトを表示してから read var を呼ぶ形に書き換えてください。
関連コマンド
echo: 変数やメッセージの出力printf: 書式付きの出力select: シェル組み込み、メニュー形式で選択を受け付け- IFS と read -r の安全読込 : 空白・改行耐性のある読み込み方
mapfile: 標準入力を配列に読み込む(複数行の一括取得)- while / until / for : while readでの実務活用
備考
readは多くのシェル(bash, zsh, ksh, dash)において シェル組み込み コマンドです。readで指定した変数は、そのシェルの環境でのみ有効。外部コマンドではありません。- パスワード入力のように非表示にする場合は
-sを利用しますが、シェルの種類によって挙動が異なる場合があります。
参考
- manページ(bash組込み): man7.org bash(1)
- GNU Bash Reference Manual: https://www.gnu.org/software/bash/manual/

コメント