← Весь справочник

read

Чтение строки из stdin или файла в переменные — опции -r -p -s -t -n -a -d, IFS и цикл по строкам

read — встроенная команда bash: читает одну строку из стандартного ввода, режет её на поля по $IFS и раскладывает по переменным. С её помощью спрашивают пользователя, читают файл построчно и разбирают строки с разделителями (/etc/passwd, CSV). Справка — help read.

Синтаксис

read [опции] [имя…]

Первое поле попадает в первую переменную, второе — во вторую, а весь остаток строки — в последнюю. Без имён строка целиком попадает в переменную REPLY.

Опции

ОпцияЧто делает
-rне обрабатывать \: обратный слэш остаётся обычным символом. Ставь почти всегда
-p 'текст'показать приглашение перед вводом — без перевода строки, в stderr и только если ввод идёт с терминала
-sне отображать вводимые символы — для паролей
-t секждать не дольше указанного времени, можно дробное (-t 0.5); при таймауте код возврата больше 128
-n Nвернуться после N символов, не дожидаясь Enter (или раньше, если встретился перевод строки)
-N Nпрочитать ровно N символов, игнорируя перевод строки
-a массивразложить поля по элементам массива, начиная с индекса 0
-d 'c'читать до символа c, а не до перевода строки; -d '' — до нулевого байта
-u fdчитать из файлового дескриптора fd вместо stdin
-eв терминале использовать readline: работают стрелки, Tab-дополнение
-i 'текст'вместе с -e: подставить начальный текст, который можно отредактировать

IFS и разбиение на поля

IFS — символы-разделители полей, по умолчанию пробел, табуляция и перевод строки.

ЗаписьЧто происходит
read a bполя по пробелам; ведущие и хвостовые пробелы срезаются; лишние поля уходят в b
IFS= read -r lineстрока целиком, без обрезки пробелов и без обработки \
IFS=: read -r a b cполя по двоеточию; IFS меняется только для этой команды
IFS=, read -ra arrполя по запятой — в массив

Код возврата

КодКогда
0строка прочитана
1конец файла (EOF) — так заканчиваются циклы while read
>128истёк таймаут -t

Примеры

# спросить имя
read -rp 'Как тебя зовут? ' name
echo "Привет, $name"

# пароль без отображения; после -s перевод строки не печатается — добавь его через echo
read -rsp 'Пароль: ' pass; echo

# подтверждение одной клавишей
read -rn 1 -p 'Продолжить? [y/N] ' ans; echo
[[ $ans == [yY] ]] || exit 1

# ответ с таймаутом и значением по умолчанию
read -rt 5 -p 'Порт [8080]: ' port || port=8080

# построчно читать файл — классическая идиома
printf 'one\n  two  \nthree\n' > /tmp/lines.txt
while IFS= read -r line; do
    echo "[$line]"
done < /tmp/lines.txt

# разобрать поля по двоеточию
while IFS=: read -r user _ uid _ _ home shell; do
    echo "$user uid=$uid home=$home"
done < /etc/passwd

# строку — в массив
IFS=, read -ra parts <<< 'red,green,blue'
echo "${#parts[@]} ${parts[1]}"     # 3 green

# читать вывод команды и сохранить переменные после цикла
count=0
while IFS= read -r f; do
    count=$((count + 1))
done < <(find /etc -maxdepth 1 -name '*.conf')
echo "найдено: $count"

# имена файлов с любыми символами: разделитель — нулевой байт
find /tmp -maxdepth 1 -type f -print0 |
    while IFS= read -r -d '' f; do echo "файл: $f"; done

# все строки сразу в массив — для этого есть mapfile
mapfile -t rows < /tmp/lines.txt
echo "${#rows[@]}"

Подводные камни

  • Без -r пропадают обратные слэши. read x превращает a\b в ab, а \ в конце строки склеивает её со следующей. Пиши read -r.
  • Без IFS= срезаются пробелы. read -r line от строки two даёт two. Для точного чтения строки — IFS= read -r line.
  • Последняя строка без \n теряется. read читает её, но возвращает 1, и цикл while read не выполняет тело. Защита: while IFS= read -r line || [[ -n $line ]]; do ….
  • Пайп в while — это подоболочка. В cmd | while read …; do n=$((n+1)); done переменная n после цикла остаётся прежней. Подавай ввод через done < <(cmd) или done < файл, либо включи shopt -s lastpipe (в скрипте).
  • echo "a b" | read x ничего не сохраняет по той же причине: read выполняется в подоболочке. Используй read x <<< "a b".
  • Команды внутри цикла съедают stdin. ssh, ffmpeg и другие программы, читающие stdin, заберут оставшиеся строки файла. Дай им < /dev/null (у ssh есть -n) или читай файл через другой дескриптор: while read -r -u 3 line; do …; done 3< файл.
  • -p, -s и -e рассчитаны на терминал. Если ввод идёт из пайпа или файла, приглашение -p не печатается, а -s ничего не меняет — read просто читает данные.

См. также

echo-printf · redirection · variables · conditions-loops · xargs