Чтение входных файлов по строке с помощью команды чтения в сценариях оболочки пропускает последнюю строку
Обычно я использую команду чтения для чтения входного файла в оболочку script по очереди. Примерный код, такой как приведенный ниже, дает неверный результат, если новая строка не вставлена в конце последней строки во входном файле blah.txt.
#!/bin/sh
while read line
do
echo $line
done <blah.txt
Итак, если входной файл читает что-то вроде -
One
Two
Three
Four
и я не получаю ответ после Four, script не может прочитать последнюю строку и печатает
One
Two
Three
Теперь, если я оставлю лишнюю пустую строку после четырех, например,
One
Two
Three
Four
//blank line
вывод выводит все строки, включая четыре. Однако это не тот случай, когда я читал строку с помощью команды cat
; все строки, включая последние, печатаются без меня, чтобы добавить лишнюю пустую строку в конце.
У кого-нибудь есть идеи, почему это происходит? Создаваемые мной сценарии в основном будут выполняться другими, поэтому нет необходимости, чтобы они добавили лишнюю пустую строку в конец каждого входного файла.
Я пытался понять это на протяжении веков; Я был бы признателен, если у вас есть какие-либо решения (конечно, команда cat
- одна, но я хотел бы знать, почему причина чтения не работает).
Ответы
Ответ 1
read
читает до тех пор, пока не найдет символ новой строки или конец файла и не возвращает нулевой код выхода, если он встречает конец файла. Поэтому вполне возможно, что он прочитает строку и вернет ненулевой код выхода.
Следовательно, следующий код небезопасен, если вход не может быть прерван новой строкой:
while read LINE; do
# do something with LINE
done
потому что тело while
не будет выполнено в последней строке.
С технической точки зрения, файл, не завершенный символом новой строки, не является текстовым файлом, а текстовые инструменты могут работать нечетными способами в таком файле. Тем не менее, я всегда неохотно отказываюсь от этого объяснения.
Один из способов решения проблемы - проверить, является ли прочитанное непустым (-n
):
while read -r LINE || [[ -n $LINE ]]; do
# do something with LINE
done
Другие решения включают использование mapfile
для чтения файла в массив, связывание файла с помощью некоторой утилиты, которая гарантированно завершает последнюю строку должным образом (grep .
, например, если вы не хотите иметь дело с пустые строки) или выполнение итеративной обработки с помощью инструмента типа awk
(который обычно является моим предпочтением).
Обратите внимание, что -r
почти наверняка необходим в встроенном read
; это заставляет read
не переосмысливать \
-последовательности на входе.
Ответ 2
DONE=false
until $DONE
do
read line || DONE=true
echo $line
done < blah.txt
Как использовать` while read` (Bash) для чтения последней строки в файле, если в конце файла нет новой строки?
Ответ 3
Один ответ:
IFS=$'\n'; for line in $(cat file.txt); do echo "$line" ; done
Ответ 4
Используйте цикл while следующим образом:
while IFS= read -r line || [ -n "$line" ]; do
echo "$line"
done <file
Или используя grep
с циклом while:
while IFS= read -r line; do
echo "$line"
done < <(grep "" file)
Использование grep .
вместо grep ""
будет пропускать пустые строки.
Примечание:
Ответ 5
Ниже код с переадресованным циклом "while-read" отлично работает для меня
while read LINE
do
let count++
echo "$count $LINE"
done < $FILENAME
echo -e "\nTotal $count Lines read"