Счётчик после while read равен нулю: где Bash запускает подоболочку

Внутри цикла журнал показывает три обработанные записи. Сразу после цикла переменная count снова равна нулю. Такое поведение легко принять за ошибку условия или случайное обнуление. Но если цикл стоит справа от вертикальной черты, сначала нужно посмотреть на границы процессов.

В обычном режиме Bash элементы конвейера выполняются в отдельных окружениях. Изменение переменной внутри такого окружения не возвращается родительской оболочке. Поэтому команда может успешно прочитать весь вход и напечатать правильные промежуточные значения, оставив внешний счётчик прежним.

Перенесём поток, сохраним цикл

Для небольшого списка удобен here-document: данные передаются в stdin цикла, а сам while остаётся в текущей оболочке. Пример считает непустые имена заданий. Перед первой записью нет пробелов, пустая строка намеренно находится посередине.

#!/usr/bin/env bash
count=0
while IFS= read -r job; do
  [[ -z "$job" ]] && continue
  count=$((count + 1))
done <<'JOBS'
import

export
validate
JOBS
printf 'jobs=%s
' "$count"

Результат — jobs=3. Перенаправление относится ко всему составному оператору while. Здесь нет производителя слева от pipe, поэтому счётчик изменяется в том же окружении, из которого затем вызывается printf.

Для настоящего файла замените here-document перенаправлением < "$input_file" после done. Если данные создаёт команда, можно использовать подстановку процесса < <(producer), но её статус нельзя считать проверенным только по успешному завершению цикла. Когда ошибка производителя должна останавливать отчёт, сначала отдельно получите и проверьте входной файл, затем разбирайте его.

Почему IFS и read тоже важны

IFS= запрещает read отбрасывать краевые пробелы, а -r сохраняет обратную косую черту буквально. В примере имена простые, но при переходе к путям или тексту эти настройки сохраняют исходные значения. Условие -z проверяет действительно пустую запись: строка из пробелов останется непустой. Если по договору пробельные строки тоже нужно пропускать, задайте это отдельным правилом.

Обратите внимание и на последнюю строку файла. Обычный read возвращает неуспех при EOF, даже если до него успел прочитать неполную строку без завершающего перевода. Для такого входа условие цикла расширяют: read -r job || [[ -n "$job" ]]. Для файлов, которые всегда заканчиваются переводом строки, исходного варианта достаточно.

Не меняйте глобальные настройки ради одного счётчика

У Bash есть настройка lastpipe, влияющая на последний элемент конвейера при выключенном управлении заданиями. Однако скрипт, зависящий от состояния чужой оболочки, сложнее передавать коллеге. Явное перенаправление показывает нужную границу непосредственно рядом с циклом.

Проверка должна сравнивать значение после done: пустой вход даёт ноль, одна непустая запись — единицу, три записи с пустой строкой — три. Печать числа внутри тела не обнаруживает потерю переменной. Если затем счётчик участвует в коде возврата или имени файла, проверяйте и этот внешний результат.

В курсе Bash Linux + Git вы отработаете переменные, циклы, перенаправления, функции и массивы на задачах. Это поможет собирать обработку файлов в понятные скрипты и сохранять изменения в Git. Откройте программу и переходите от отдельных команд к законченной автоматизации.

Продолжить на практике