цикл while read останавливается после первой строки, даже если он не считывает данные со стандартного ввода

цикл while read останавливается после первой строки, даже если он не считывает данные со стандартного ввода

У меня есть файл, который testназывается

test
test

и я запускаю эту команду

while read line
do
echo "$line"
done </tmp/test

Это должно вывести "test" два раза, но выводит только один раз. Использование while IFS= read -r lineничего не меняет. Что-то, что исправляет это, это вывод третьей пустой строки, но это должно работать в любом случае.

решение1

Пытаться:

while IFS='' read -r line || [ "$line" ]

Да, чтение происходит со стандартного ввода (stdin), <testперенаправление происходит на stdin.

Возможно, read предназначен для работы с текстовыми файлами.Правильные текстовые файлы должны заканчиваться на новой строке..

СделатьКонец файла на новой строке оказывается очень быстрым:

[ -n "$(tail -c1 file)" ] && printf '\n' >>file 

Но если вы не можете отредактировать файл, чтобы добавить новую строку (если она отсутствует), то решением будет проверить (дополнительно), было ли что-то прочитано:

while read line || [ "$line" ]

Это заставит цикл выполниться, если чтение не удалось (достигнут конец файла без чтения новой строки (по умолчанию)), но что-то было прочитано в (в "$line").

Это будет работать правильно для файлов, заканчивающихся на новую строку или нет.

Нет, a IFS='' readне будет (напрямую) влиять на чтение последней строки, на самом деле IFS=''(по сравнению с IFS по умолчанию[а]пробела, табуляции, новой строки) повлияет только на разделение нанесколькопеременные (помимо влияющих на удаление начальных и/или конечных пробелов (если IFS содержит только пробелы)[б])). Поскольку имеется только одна переменная ( "$line"), то разделение выполнять не нужно.

Опция -dread (в bash с версии 2.04) тоже не поможет. Нет определенного символа "конец файла" для сопоставления (последний байт может быть любым).

Остаются только два варианта:

  • восстановить файл, чтобы сделать его корректным текстовым файлом
  • проверить, было ли что-либо считано в переменную line.

Правильный сценарий тогда будет таким:

#!/bin/bash
while IFS='' read -r line || [ "$line" ]
do
    printf '%s\n' "$line"
done <test

IFS='' используется для избежания проблем с необычными значениями IFS.


[а][б]Конечно, если IFS может иметь какое-либо значение, многие другие символы могут быть удалены. Попробуйте (для значений, которые заканчиваются на 'x')

printf "test\ntesx\ntest" | while IFS="x" read -r line || [ "$line" ]; do echo "$line"; done

Нет, zsh делает что-то другое.

Связанный контент