Shell-цикл пропустил последнюю запись: читаем файл без завершающего newline
В файле со списком заданий три записи, но shell-цикл выполняется только два раза. Редактор показывает третью строку, имя в ней корректно. Разница становится видна при просмотре байтов: после последней записи нет перевода строки. read получает данные, присваивает переменную, затем встречает EOF и возвращает ненулевой статус. Обычный while проверяет именно статус, поэтому тело для последнего фрагмента не выполняется.
Если источник обязан выдавать текстовые файлы с завершающим newline, отсутствие этого символа можно считать нарушением формата. Но списки из внешнего API, ручных выгрузок и некоторых шаблонов часто приходят без него. Тогда обработчик должен явно принять последнюю непустую запись.
Добавим условие для остатка данных
Пример создаёт файл, где после beta нет newline. Он ничего не запускает по именам из входа, а только показывает реально прочитанные значения.
printf 'alpha\nbeta' > tasks.txt
count=0
while IFS= read -r line || [ -n "$line" ]; do
count=$((count + 1))
printf '%s: <%s>\n' "$count" "$line"
done < tasks.txt
printf 'Всего: %s\n' "$count"
Должны появиться alpha, beta и итог Всего: 2. Дополнительная проверка разрешает выполнить тело, когда read вернул неуспех, но успел присвоить непустой остаток. На следующей попытке EOF переменная станет пустой, и цикл завершится.
IFS= запрещает удаление ведущих и завершающих символов, которые иначе считались бы разделителями. -r запрещает специальную обработку обратной косой черты. Вместе они делают read подходящим для чтения строк без случайного изменения пути или текстового значения. Кавычки при дальнейшей передаче line всё равно обязательны.
Пустые строки остаются частью формата
Пустая строка, завершённая newline, успешно читается и попадает в тело цикла. Если она означает «пропустить», добавьте явную проверку внутри тела. Если список должен содержать только непустые задания, лучше вернуть ошибку с номером строки. Удалять пустоту автоматически опасно, когда она обозначает пропущенное обязательное значение.
У последнего отсутствующего newline нет отдельной «пустой записи» после EOF. Например, файл с alpha и завершающим newline содержит одну строку, а не две. Это важно при сравнении количества прочитанных записей с числом разделителей: wc -l считает переводы строк и для незавершённого последнего фрагмента может показать меньше, чем число логических записей вашего обработчика.
Пример использует перенаправление файла в цикл. Оно сохраняет счётчик в текущей оболочке. Если переписать вход как cat tasks.txt | while ..., некоторые оболочки выполнят цикл в подоболочке и итоговый count снаружи не изменится. Это отдельная особенность shell, которая может замаскировать уже исправленную обработку EOF.
Проверьте источник, а затем действие
Для проверки достаточно четырёх файлов: пустого, одной строки с newline, одной без newline и двух строк с пустой записью между ними. Запишите ожидаемое количество обработок для каждого. Если задания потом отправляются во внешнюю систему, сначала оставьте printf: так легко увидеть дублирование либо пропуск без побочных эффектов.
read обрабатывает текстовые строки, а не произвольные бинарные данные. Нулевые байты и вложенные переводы строк требуют другого формата и подходящего парсера. Для списка имён файлов, где newline допустим внутри имени, используйте нулевой разделитель и совместимые инструменты, а не договорённость «один файл на строку».
В программе Linux и Bash корректный quoting и проверка ошибок связаны с практической автоматизацией. Откройте программу и продолжите к обработчику заданий, который согласует формат входа, число выполненных операций и итоговый статус для коллеги или расписания.
Ещё по теме
Продолжить на практике
- Linux и Bash для DevOps-инженеров ↗
Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.
