
У меня есть файл, который 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"
), то разделение выполнять не нужно.
Опция -d
read (в 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 делает что-то другое.