Как обрабатывать отсутствие переменной окружения при использовании set -u

Вопрос или проблема

У меня есть скрипт на bash, который я должен иметь возможность запускать из:

  1. cron
  2. интерактивной (входной) оболочки

Этому скрипту нужно знать, был ли он запущен из cron или из интерактивной оболочки. Я думал, что решил эту проблему, когда объявил переменную среды в root crontab:

RUN_BY_CRON="TRUE"

В скрипте я проверяю RUN_BY_CRON и использую результат для установки другой переменной:

if [ "$RUN_BY_CRON" = "TRUE" ]; then
    ((wait_time=DELAY_HALT*60))
fi

Это работало, пока я не добавил set -u в мой скрипт (как “общая стратегия защитного программирования”). С тех пор, когда я запускаю скрипт из командной строки, set -u отмечает RUN_BY_CRON как ошибку “неинициализированной переменной”:

$ sudo ./skedrtc.sh
./skedrtc.sh: line 24: RUN_BY_CRON: unbound variable

FWIW, я запускал shellcheck на этом скрипте и не получил никаких предупреждений или ошибок.

Я пробовал добавить проверку для RUN_BY_CRON, но получил ту же ошибку. Я пробовал проверять на интерактивную оболочку, но тестирование внутри самого скрипта не помогает:

...
if [ -z "$RUN_BY_CRON" ]; then        # проверка на пустую строку
    RUN_BY_CRON="FALSE"
fi
...

if [[ $- == *i* ]]; then              # проверка на интерактивную оболочку
    RUN_BY_CRON="FALSE"
fi

Это похоже на ситуацию “catch 22”. Я искал способы создать блоки try-catch, но, насколько я понимаю, в bash такого нет.

В: Как я могу избежать этой ошибки “неинициализированной переменной” без удаления set -u ?

Возможно, вы могли бы использовать синтаксис ${var-"string"}, который означает, что если $var не установлена, то возвращается “строка”, в противном случае возвращается $var

например

#!/bin/bash

set -u

echo ${NOSUCHVARIABLE-"FALSE"}

Теперь, если я запускаю это обычно:

% ./x
FALSE

Так нет ошибки, и значение разворачивается в “значение по умолчанию”.

Но если значение установлено:

% NOSUCHVARIABLE=hello ./x
hello

Это даже работает, если переменная установлена, но пуста

% NOSUCHVARIABLE="" ./x

Если вы также хотите заменить пустую строку, используйте :- вместо этого;

#!/bin/bash

set -u

echo ${NOSUCHVARIABLE:-"FALSE"}

% NOSUCHVARIABLE="" ./x
FALSE

Не редкий шаблон – заставить код сделать что-то вроде:

NOSUCHVARIABLE=${NOSUCHVARIABLE-""}

в начале, чтобы гарантировать, что есть значение по умолчанию.

Ответ или решение

Вопрос, связанный с устранением ошибки "unbound variable" при использовании команды set -u в Bash-скрипте, который должен уметь запускаться как из cron, так и из интерактивной оболочки, является действительно интересной задачей, особенно в контексте применения защитных стратегий программирования. В данной ситуации изучение механизмов обработки переменных окружения и применение альтернативных стратегий для работы с unset-переменными является ключевым.

Теоретическая основа

Для начала важно понимать, что команда set -u в Bash нацелена на выявление и предотвращение использования необъявленных переменных, что помогает избежать потенциальных ошибок в логике скрипта. Это особенность, которая повышает надежность кода, заставляя программиста явным образом определять все используемые переменные. Однако, это может создать проблемы, когда определенные переменные могут или не могут быть установлены в зависимости от контекста, в котором скрипт работает.

В вашей ситуации, когда вы хотите определить, был ли скрипт запущен из cron с использованием переменной среды RUN_BY_CRON, её отсутствие в интерактивной оболочке вызывает ошибку при включенной опции set -u. Для устранения этой проблемы полезным будет использование синтаксиса замещения значений по умолчанию для переменных.

Пример

Одним из эффективных решений данной задачи является использование параметрического замещения в Bash. Существует несколько вариантов, позволяющих безопасно разбирать unset-переменные, предоставляя им значения по умолчанию:

  1. ${VAR:-default} – Возвращает значение переменной VAR, если она установлена и не пуста; в противном случае возвращает default.

  2. ${VAR-"default"} – Возвращает значение переменной VAR, если она установлена; в противном случае возвращает default.

Например, приведенный ниже код покажет, как использовать эти подходы:

#!/bin/bash

set -u

# Проверка и задание значения для переменной, если она не определена
RUN_BY_CRON=${RUN_BY_CRON:-"FALSE"}

if [ "$RUN_BY_CRON" = "TRUE" ]; then
    ((wait_time=DELAY_HALT*60))
fi

В этом примере, если RUN_BY_CRON не определена в окружении, она автоматически получает значение "FALSE". Это предотвращает возникновение ошибки "unbound variable", сохраняя при этом общую структуру и логику работы скрипта нетронутой.

Применение в текущем контексте

Применение предложенного решения позволяет успешно выйти из "catch-22" ситуации, описанной вами. Разработка скриптов с учетом различных контекстов исполнения требует внимательного подхода к обработке переменных, и использование механизма замещения значений по умолчанию в Bash является одним из наиболее эффективных способов решения проблемы с необъявленными или отсутствующими переменными.

Важно, что такой подход не только устраняет конкретную ошибку, но и обеспечивает большую надежность и предсказуемость поведения скрипта. Это особенно важно в сценариях, где скрипт должен работать в различных средах, например, как из cron, так и из интерактивной оболочки. Использование опции set -u, в этом контексте, становится гарантией того, что мы не сталкиваемся с неожиданным поведением из-за ошибок в именовании переменных.

Заключительно, стоит отметить, что обработка переменных окружения и борьба с потенциальными ошибками — важная часть написания надежного и поддерживаемого кода на Bash. Внедрение подходов, подобных вышеописанным, демонстрирует высокий уровень внимания к деталям и способствует созданию программного обеспечения, которое выдерживает строгие требования к качеству и безопасности.

Оцените материал
Добавить комментарий

Капча загружается...