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