Вопрос или проблема
У меня есть скрипт на bash
, который я должен иметь возможность запускать из:
cron
- интерактивной (входной) оболочки
Этому скрипту нужно знать, был ли он запущен из 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-переменные, предоставляя им значения по умолчанию:
-
${VAR:-default}
– Возвращает значение переменнойVAR
, если она установлена и не пуста; в противном случае возвращаетdefault
. -
${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. Внедрение подходов, подобных вышеописанным, демонстрирует высокий уровень внимания к деталям и способствует созданию программного обеспечения, которое выдерживает строгие требования к качеству и безопасности.