Рассмотрим следующий простой bash-скрипт
cd nowhere
Это приводит к сообщению об ошибке, что-то вроде
my-script.sh: line 1: cd: nowhere: No such file or directory
Все идет нормально. Это имеет смысл. Давайте немного подправим это, обернув компакт-диск в функцию:
function go_nowhere { cd nowhere; }
go_nowhere
Мы получаем то же сообщение об ошибке. Это все еще имеет смысл. Давайте попробуем подоболочку вместо использования функции.
bash -c 'cd nowhere'
Опять же, аналогичное сообщение об ошибке, но теперь bash «подписывает» сообщение об ошибке своим собственным именем, а не именем нашего скрипта:
bash: line 1: cd: nowhere: No such file or directory
Пока что все еще имеет смысл. Теперь давайте сделаем и то, и другое! Мы обернем cd функцией и запустим эту функцию в подоболочке:
function go_nowhere { cd nowhere; }
export -f go_nowhere
bash -c 'go_nowhere'
Теперь сообщение немного другое. Во-первых, номер строки — «строка 0», а не строка 1. Это имеет смысл: поскольку мы использовали «экспорт», это означает, что с точки зрения подоболочки функция была определена «перед первой строкой», поскольку она была унаследована от его родитель. Но другое отличие более странное:
environment: line 0: cd: nowhere: No such file or directory
Что такое environment
? Я не запускаю программу под названием «среда», так почему же bash печатает сообщение об ошибке, которое утверждает, что исходит из такой программы? Он пытается сказать мне, что ошибка находится в функции, унаследованной bash от среды? Мы можем проверить эту теорию:
function go_nowhere { cd nowhere; }
bash -c "`declare -f`; go_nowhere"
Номер строки снова стал больше, но сообщение об ошибке по-прежнему «подписано» как environment
:
environment: line 3: cd: nowhere: No such file or directory
Теперь я действительно этого не понимаю. Он НЕ получил эту функцию из среды. Так откуда же берется строка environment
? Обратите внимание, что это не относится только к сценариям... это можно воспроизвести в интерактивном режиме из приглашения: об ошибке внутри функции в подоболочке сообщается от environment
, и я понятия не имею, почему
Расширение комментария Шона: export -f
в родительской оболочке сохраняет функцию в среде этой оболочки (как переменную env со странным именем), и когда вы запускаете bash
как подпроцесс (примечание: технически это не подоболочка, это немного отличается) она наследует эту среду и извлекает из нее определение функции. Итак, с точки зрения подпроцесса функция действительно пришла из окружающей среды.
@Шон: Я думаю, что точка зрения ОП заключается в том, что, хотя bash -c
изначально получил функцию из среды, он впоследствии переобъявил ее (когда выполнил вывод declare -f
), а это означает, что новая версия не из среды. (Очевидно, что-то в этом рассуждении не работает, но оно звучит правильно!)
Что такое окружающая среда?
Это жестко запрограммированная строка в исходном коде bash здесь: https://github.com/bminor/bash/blob/master/make_cmd.c#L804.
/* Assume that shell functions without a source file before the shell is
initialized come from the environment. Otherwise default to "main"
(usually functions being defined interactively) */
if (temp->source_file == 0)
temp->source_file = shell_initialized ? "main" : "environment";
Не используйте обратные кавычки. Предпочитаю $(..)
. Проверьте свой код с помощью Shellcheck.
Вы можете получить другую строку "main"
, прочитав ее из стандартного ввода. shell_initialized
означает, что оболочка готова читать интерактивный ввод.
$ LC_ALL=C bash -s <<<"$(declare -f go_nowhere); go_nowhere"
main: line 3: cd: nowhere: No such file or directory
Как мы можем объяснить вывод этой команды: LC_ALL=C bash -s <<<"go_nowhere; $(declare -f go_nowhere); go_nowhere"
привет @Philippe Я не понимаю. Что тут объяснять? Что требует объяснения? Что вас смущает в выводе? Какой результат вы получаете? github.com/bminor/bash/blob/master/error.c#L96 тоже актуально.
На выходе одна строка содержит environment
, другая main
. Если shell_initialized
один и тот же при запуске обоих, как мы можем объяснить разницу?
Вы экспортировали функцию export -f go_nowhere
, поэтому первоначальный вызов поступает из среды, загружаемой при запуске bash до установки shell_initialized
, до загрузки интерактивности. Напротив, если вы не экспортируете, в первой строке будет bash: line 1: go_nowhere: command not found
. shell_initailized
устанавливается довольно поздно github.com/bminor/bash/blob/master/shell.c#L822 . Я подозреваю, что функции загружаются где-то в shell_initialize()
Так что это всего лишь эвристика, и в данном случае она дала осечку. Имеет смысл. Спасибо!!
И я действительно использую шеллчек, это спасение... Но я игнорирую это конкретное предупреждение... синтаксис обратной апострофа намного проще! Он менее визуально навязчив (на самом деле можно утверждать, что он почти невидим и, следовательно, СЛИШКОМ ненавязчив). И пальцам легче (0 сдвинутых символов против 3). Вложить это — полная катастрофа, но я все равно этого не делаю. (Оказывается, обратная косая черта тоже ведет себя странно... не осознавал этого.)
Он НЕ получил эту функцию из окружающей среды, но она получила!
export -f
сохраняет определения функций в переменных среды.