Пустая папка запустила обработку файла *.json: задаём политику glob в Bash
Ночной импорт иногда получает пустую папку: поставщик пропустил выгрузку, либо за день действительно не было изменений. Скрипт всё равно вызывает загрузчик и передаёт ему имя incoming/*.json. Ошибка загрузчика выглядит правдоподобно: «файл не найден». Но файл с таким именем никто не обещал создавать. Оболочка оставила шаблон без изменения, потому что совпадений не оказалось.
Ошибка особенно неприятна в повторно используемой автоматизации. На стенде всегда лежал один образец, поэтому ветку без файлов никто не видел. После переноса обработчик начал считать пустую поставку повреждённой. Исправление начинается с решения: пустота допустима или отсутствие файлов означает сорванную поставку? От этого зависит код завершения задания.
Сначала получим настоящий список
В Bash удобно раскрыть шаблон в массив после включения nullglob. Эта настройка удаляет не совпавший шаблон из списка слов. Массив сохраняет каждое имя отдельным элементом. Пример работает в текущем каталоге; для репетиции создайте отдельную пустую папку и запустите код из неё.
#!/usr/bin/env bash
shopt -s nullglob
files=(./incoming/*.json)
if ((${#files[@]} == 0)); then
printf '%s\n' 'Нет новых файлов'
exit 0
fi
for file in "${files[@]}"; do
printf 'Проверяем: %s\n' "$file"
done
При отсутствии incoming либо файлов нужного расширения код напечатает «Нет новых файлов». При двух подходящих именах тело цикла выполнится два раза. Строка с пробелами внутри имени останется одним аргументом printf. Префикс ./ также помогает дальнейшим командам отличать относительный путь от параметра, начинающегося с дефиса.
Здесь сознательно выбрана мягкая политика. Если выгрузка обязательна, поменяйте сообщение на диагностическое, направьте его в stderr и завершите ветку с exit 1. В этом случае расписание сможет сообщить о пропуске. Само nullglob не принимает решение об успешности: оно только даёт удобное представление пустого набора.
Отсутствующая папка — отдельная ситуация
Одинаковый пустой массив получится и при пустом каталоге, и при неверно заданном пути. Для задания, где каталог должен существовать, до раскрытия шаблона проверьте [[ -d ./incoming ]]. Если это входной mount или результат предыдущего шага доставки, отсутствие каталога обычно следует считать ошибкой конфигурации. Иначе опечатка в пути будет каждую ночь проходить как спокойный день без изменений.
Не включайте dotglob автоматически. Обычная маска *.json не выбирает скрытые имена, начинающиеся с точки. Если поставщик использует скрытые временные файлы, их исключение бывает полезным: обрабатываются только опубликованные выгрузки. Если скрытые имена входят в контракт, политику надо изменить явно и добавить такой файл в проверку.
Где ограничить настройку
shopt меняет состояние текущей оболочки. В отдельном исполняемом скрипте это предсказуемо: настройка живёт до завершения процесса. В библиотечной функции, подключённой через source, она способна неожиданно повлиять на вызывающий код. Такую обработку можно выполнять в подоболочке, оформив тело функции круглыми скобками. После выхода исходные настройки вызывающей оболочки сохранятся.
failglob подходит для другой политики: отсутствие совпадений становится ошибкой раскрытия, и команда не запускается. Для импорта, которому нужно написать собственное сообщение или различить допустимую пустоту и ошибку поставки, массив с nullglob обычно проще читать и тестировать.
Перед подключением к загрузчику проверьте три входа: пустую папку, один файл и два файла, один из которых содержит пробел. Затем замените printf реальной командой и отдельно определите поведение при ошибке одного документа: остановить всю поставку или продолжить с итоговым ненулевым статусом.
В курсе «Linux и Bash для DevOps-инженеров» работа с quoting и проверкой ошибок связана с повседневной автоматизацией. Откройте программу и продолжите от этого примера к скрипту, который проверяет вход, обрабатывает поставку и выдаёт понятный результат для расписания.
Ещё по теме
Продолжить на практике
- Linux и Bash для DevOps-инженеров ↗
Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.
