Пустая папка запустила обработку файла *.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 и разберите типичные сбои сервиса — от занятого порта до переполненного диска.