Пустая переменная отключила значение по умолчанию: различаем unset и empty
Скрипт принимает имя окружения из переменной RELEASE_ENV. Если настройка отсутствует, нужно использовать staging. После переноса в CI переменная существует, но содержит пустую строку, и приложение получает пустое имя. Причина в выбранной форме подстановки: shell различает неустановленный параметр и параметр с пустым значением.
Это различие полезно. Пустая строка иногда означает явное действие: убрать префикс или отключить необязательную подпись. В других настройках она недопустима. Прежде чем расставлять значения по умолчанию, определите смысл каждого состояния: отсутствует, задано пусто, задано содержательно.
Проверим две формы рядом
Пример работает в POSIX shell и не меняет файлы. Он показывает поведение на неустановленном параметре, пустой строке и обычном значении.
unset RELEASE_ENV
printf 'unset: <%s> <%s>\n' "${RELEASE_ENV-staging}" "${RELEASE_ENV:-staging}"
RELEASE_ENV=''
printf 'empty: <%s> <%s>\n' "${RELEASE_ENV-staging}" "${RELEASE_ENV:-staging}"
RELEASE_ENV=production
printf 'value: <%s> <%s>\n' "${RELEASE_ENV-staging}" "${RELEASE_ENV:-staging}"
В первой строке оба результата — staging. Во второй форма без двоеточия даёт пустую строку, а с двоеточием — staging. В третьей обе формы сохраняют production. Двоеточие добавляет проверку на пустоту; без него проверяется только факт установки параметра.
Эти выражения сами по себе не присваивают RELEASE_ENV. Они вычисляют значение для конкретного места использования. Если скрипт обращается к настройке много раз, можно один раз записать нормализованное значение в отдельную переменную: release_env=${RELEASE_ENV:-staging}. Тогда дальнейшая логика использует один проверенный результат.
Не каждой настройке нужен запасной вариант
Для идентификатора выпуска или пути назначения молчаливый fallback может скрыть ошибку вызова. Форма ${RELEASE_ID:?Укажите RELEASE_ID} завершает неинтерактивную оболочку с диагностикой, если переменная отсутствует или пуста. Проверку следует делать в начале скрипта, до подготовки файлов и запуска внешних операций.
Если пустая строка допустима, а отсутствие нет, используйте вариант без двоеточия: ${LABEL?Укажите LABEL}. В обоих случаях сообщение должно называть конкретный параметр и ожидаемое действие. Общая ошибка «bad config» заставит коллегу искать, какая из десятка настроек не передана.
set -u ловит обращения к неустановленным параметрам, но не запрещает пустые значения. Это другая задача. Даже с nounset обязательное имя окружения стоит проверять содержательно: строка может существовать и быть пустой либо содержать пробелы. Parameter expansion задаёт только первую границу проверки, а допустимый набор значений проверяется отдельно.
Обязательность не равна валидности
Непустое значение prodution пройдёт проверку :?, хотя такого окружения нет. После извлечения параметра полезно использовать case с явными staging и production. Ошибка должна возникать до действия, которое отправляет артефакт или меняет конфигурацию. Для свободного текста нужен иной валидатор; универсального «правильного непустого значения» не существует.
Не смешивайте проверку с выводом секрета. Сообщение об отсутствующем токене может назвать переменную, но логировать её значение для диагностики не нужно. Для имени окружения печать нормализованного значения обычно полезна: инженер видит, куда направлена операция.
Заведите три небольших запуска: без переменной, с пустой строкой и с заданным именем. Для обязательных параметров добавьте неправильное непустое значение. Такой набор проверяет контракт вызова значительно лучше, чем единственный успешный запуск из вашей интерактивной оболочки.
В курсе Linux и Bash для DevOps-инженеров изучаются безопасная автоматизация и проверка ошибок. Откройте программу и продолжите к скрипту сопровождения, который проверяет настройки до начала работы и объясняет причину отказа тому, кто запустил его из расписания или CI.
Ещё по теме
Продолжить на практике
- Linux и Bash для DevOps-инженеров ↗
Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.
