Подтвердить что ты не робот

Чтение входных файлов по строке с помощью команды чтения в сценариях оболочки пропускает последнюю строку

Обычно я использую команду чтения для чтения входного файла в оболочку 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 - одна, но я хотел бы знать, почему причина чтения не работает).

4b9b3361

Ответ 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 не переосмысливать \ -последовательности на входе.

Ответ 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"