Вопрос или проблема
Я нашел следующее в man bash
как определение интерактивного шелла:
Интерактивный шелл — это шелл, запущенный без не-опционных аргументов (если -s не указан) и без опции -c, стандартные ввод и вывод ошибок которого оба подключены к терминалам (как определяется isatty(3)), или шелл, запущенный с опцией -i. PS1 устанавливается и $- включает i, если bash интерактивный, позволяя проверять это состояние в файле сценария или в файле запуска.
Таким образом, интерактивный шелл — это тот, который:
- не имеет не-опционных аргументов (если
-s
не указано), не имеет опции-c
и стандартные ввод и вывод ошибок которого оба подключены к терминалу; или - имеет опцию
-i
Я написал следующий пример:
bash << EOF
echo '$-'
EOF
и он выдает следующий вывод:
himBHs
Таким образом, похоже, это интерактивный шелл (он содержит i
). Однако мне не ясно, как он может быть интерактивным, когда стандартный ввод в этом случае не является терминалом… В документации для этого типа перенаправления (here documents
) написано следующее:
Этот тип перенаправления инструктирует шелл читать ввод из текущего источника до тех пор, пока не будет встречена строка, содержащая только разделитель (без завершающих пробелов). Все строки, прочитанные до этого момента, затем используются как стандартный ввод (или файловый дескриптор n, если n указан) для команды.
Это говорит о том, что стандартный ввод — это все строки, прочитанные до разделителя (я предполагаю, что они затем передаются команде, перенаправляя стандартный ввод команды в канал или что-то подобное). Следовательно, стандартный ввод не является (подключенным к) терминалом.
Почему это происходит? Почему bash интерактивный в этом случае?
Почему это происходит? Почему bash интерактивный в этом случае?
Это не так.
Внешний – интерактивный – шелл расширяет $-
(кавычки не имеют особого значения в этом контексте – сравните bash -c "echo '$-'"
) и передает echo 'himBHs'
внутреннему шеллу. Вы можете убедиться в этом, заменив bash
на bash -v
:
$ bash -v <<EOF
echo '$-'
EOF
echo 'himBHs'
himBHs
Вы можете предотвратить это, используя символ обратной косой черты для экранирования $
$ bash -v <<EOF
echo \$-
EOF
echo $-
hvBs
или закавычить разделитель heredoc
$ bash -v <<'EOF'
echo $-
EOF
echo $-
hvBs
.
Ответ или решение
Вопрос о том, почему Bash с редирекцией "here documents" может начинаться как интерактивный, требует глубокого понимания того, как Bash интерпретирует команды и каким образом он определяет, является ли оболочка интерактивной.
Теория
Интерактивная оболочка в Bash определяется несколькими условиями. Согласно документации, интерактивная оболочка — это та, которая:
- Запущена без не-опциональных аргументов (за исключением случая, когда указана опция
-s
), без опции-c
и стандартный ввод и ошибка которой подключены к терминалам (определяется с помощьюisatty(3)
), или - Запущена с опцией
-i
.
В интерактивной оболочке переменная окружения PS1
устанавливается, а в переменной \$-
содержится символ i
, что указывает на интерактивность. Однако, ключевым моментом здесь является то, что интерпретация этих условий около специфическая и может вызвать путаницу, как это происходит в вашем примере с here document.
Пример
Сначала рассмотрим пример вашего кода:
bash << EOF
echo '$-'
EOF
На первый взгляд может показаться, что эта конструкция создает интерактивную оболочку, потому что вывод содержит i
. Однако, как оказалось, интерактивность приходит не из внутренней оболочки, а от внешней. Внешняя оболочка интерпретирует и подставляет значение $-
, которое затем передается как аргумент внутренней оболочке echo 'himBHs'
. Именно это значение и отображается в результате.
Применение
Чтобы проверить, является ли указанная вами оболочка действительно интерактивной, можно использовать флаг -v
для повышения уровня детализации вывода:
bash -v << EOF
echo '$-'
EOF
Выведенный результат (echo 'himBHs'
) показывает, что echo
видит уже интерпретированную строку, что лишний раз доказывает, что обработка происходит на уровне внешней оболочки.
Избежание путаницы
-
Чтобы избежать подобной путаницы и протестировать истинное состояние внутренней оболочки, стоит экранировать специальный символ
$
, либо использовать кавычки вокруг here document:С экранированием $:
bash -v << EOF echo \$- EOF
С использованием кавычек:
bash -v << 'EOF' echo $- EOF
В обоих случаях внутренний вызов echo
видит необработанную строку и выводит результат, отражающий истинное состояние запускаемого скрипта.
Заключение
Таким образом, путаница с интерактивностью оболочки зачастую может возникать из-за различий в интерпретации кода внешней и внутренней оболочками. Использование here document само по себе не делает оболочку интерактивной; это зависит от специфики вызова и используемых интерпретатором механизмов подстановки. Правильное экранирование и понимание того, как и когда shell-интерпретаторы оценивают переменные и команды, позволяет избежать недоразумений и корректно интерпретировать результат выполнения скриптов.