Для чего нужна переменная $BASH_COMMAND?

Для чего нужна переменная $BASH_COMMAND?

СогласноРуководство по Башу, переменная окружения BASH_COMMANDсодержит

Команда, которая в данный момент выполняется или будет выполнена, если только оболочка не выполняет команду в результате прерывания; в этом случае это команда, выполняющаяся во время прерывания.

Оставив в стороне этот случай с ловушкой, если я правильно понимаю, это означает, что когда я выполняю команду, переменная BASH_COMMANDсодержит эту команду. Не совсем ясно, сбрасывается ли эта переменная после выполнения команды (т.е. доступна толькопокакоманда выполняется, но не после), хотя можно утверждать, что поскольку это «командав настоящее времяказнен иливот-вот будетвыполнено", это не командаэто было простоказнен.

Но давайте проверим:

$ set | grep BASH_COMMAND=
$ 

Пусто. Я ожидал увидеть BASH_COMMAND='set | grep BASH_COMMAND='или, может быть, просто BASH_COMMAND='set', но пусто меня удивило.

Давайте попробуем что-нибудь еще:

$ echo $BASH_COMMAND
echo $BASH_COMMAND
$ 

Ну, это имеет смысл. Я выполняю команду echo $BASH_COMMAND, и переменная BASH_COMMANDсодержит строку echo $BASH_COMMAND. Почему в этот раз это сработало, а раньше — нет?

Давайте сделаем setэто еще раз:

$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

Так что подождите. Этобылустановлено, когда я выполнил эту echoкоманду, и этоне былоСбросил потом. Но когда я setснова выполнил,BASH_COMMAND не былоустановлена ​​в setкоманду. Как бы часто я ни выполнял setкоманду здесь, результат остается тем же. Так, переменная установлена ​​при выполнении echo, но не при выполнении set? Давайте посмотрим.

$ echo Hello AskUbuntu
Hello AskUbuntu
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$

Что?Итак, переменная была установлена, когда я выполнил echo $BASH_COMMAND, нонеткогда я выполнил echo Hello AskUbuntu? Где теперь разница? Переменная устанавливается только тогда, когда сама текущая команда фактически заставляет оболочку вычислять переменную? Давайте попробуем что-нибудь другое. Может быть, на этот раз какая-то внешняя команда, не встроенная в bash, для разнообразия.

$ /bin/echo $BASH_COMMAND
/bin/echo $BASH_COMMAND
$ set | grep BASH_COMMAND=
BASH_COMMAND='/bin/echo $BASH_COMMAND'
$

Хм, ладно... опять же, переменная была установлена. Так что, моя текущая догадка верна? Переменная устанавливается только тогда, когда ее нужно оценить? Почему?Почему?Из соображений производительности? Давайте сделаем еще одну попытку. Мы попробуем выполнить grep для $BASH_COMMANDфайла, и поскольку $BASH_COMMANDshould затем содержит grepкоманду, grepshould выполнить grep для этой grepкоманды (т.е. для себя). Итак, давайте создадим соответствующий файл:

$ echo -e "1 foo\n2 grep\n3 bar\n4 grep \$BASH_COMMAND tmp" > tmp
$ grep $BASH_COMMAND tmp
grep: $BASH_COMMAND: No such file or directory
tmp:2 grep                                      <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the word "grep" is RED
tmp:2 grep                                      <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp                    <-- here, the word "grep" is RED
$ set | grep BASH_COMMAND=
BASH_COMMAND='grep --color=auto $BASH_COMMAND tmp'
$

Хорошо, интересно. Команда grep $BASH_COMMAND tmpбыла расширена до grep grep $BASH_COMMAND tmp tmp(переменная, конечно, расширяется только один раз), и поэтому я выполнил grep для grep, один раз в файле $BASH_COMMAND, который не существует, и дважды в файле tmp.

В1:Верны ли мои текущие предположения о том, что:

  • BASH_COMMANDустанавливается только тогда, когда команда пытается фактически оценить его; и
  • этонетсбрасывается после выполнения команды, даже если описание заставляет нас так думать?

В2:Если да, то почему? Производительность? Если нет, то как еще можно объяснить поведение в приведенной выше последовательности команд?

В3:Наконец, есть ли какой-либо сценарий, в котором эта переменная могла бы быть действительно осмысленно использована? На самом деле я пытался использовать ее внутри $PROMPT_COMMANDдля анализа выполняемой команды (и делать что-то в зависимости от этого), но не могу, потому что как только в моем $PROMPT_COMMAND, я выполняю команду для просмотра переменной $BASH_COMMAND, переменная получает значения этой команды. Даже когда я делаю это MYVARIABLE=$BASH_COMMANDв самом начале моего $PROMPT_COMMAND, тогда MYVARIABLEсодержит строку MYVARIABLE=$BASH_COMMAND, потому что присваивание тоже является командой. (Этот вопрос не о том, как я могу получить текущую команду в ходе $PROMPT_COMMANDвыполнения. Я знаю, что есть и другие способы.)

Это немного похоже на принцип неопределенности Гейзенберга. Просто наблюдая за переменной, я ее меняю.

решение1

Отвечая на третий вопрос: конечно, его можно использовать осмысленно, как это явно указано в руководстве по Bash – в ловушке, например:

$ trap 'echo ‘$BASH_COMMAND’ failed with error code $?' ERR
$ fgfdjsa
fgfdjsa: command not found
‘fgfdjsa’ failed with error code 127
$ cat /etc/fgfdjsa
cat: /etc/fgfdjsa: No such file or directory
‘cat /etc/fgfdjsa’ failed with error code 1

решение2

Теперь, когда на вопрос 3 дан ответ (правильный, на мой взгляд: BASH_COMMANDон полезен в ловушках и вряд ли где-либо еще), давайте попробуем ответить на вопросы 1 и 2.

Ответ на вопрос 1: правильность вашего предположения неразрешима. Истинность ни одного из пунктов списка не может быть установлена, поскольку они спрашивают о неопределенном поведении. Согласно спецификации, значение BASH_COMMANDустанавливается в текст команды на время выполнения этой команды. В спецификации не указано, каким должно быть его значение в любой другой ситуации, т. е. когда никакая команда не выполняется. Оно может иметь любое значение или вообще не иметь его.

Ответ на вопрос 2 "Если нет, как еще можно объяснить поведение в приведенной выше последовательности команд?" следует логически (хотя и несколько педантично): он объясняется тем, что значение BASH_COMMANDне определено. Поскольку его значение не определено, оно может иметь любое значение, что и показывает последовательность.

Постскриптум

Есть один момент, где, как мне кажется, вы действительно наткнулись на слабое место в спецификации. Это где вы говорите:

Даже когда я делаю MYVARIABLE=$BASH_COMMAND прямо в начале $PROMPT_COMMAND, то MYVARIABLE содержит строку MYVARIABLE=$BASH_COMMAND,потому что задание — это тоже команда.

То, как я прочитал страницу руководства bash, часть, выделенная курсивом, неверна. В разделе SIMPLE COMMAND EXPANSIONобъясняется, как сначала откладываются назначения переменных в командной строке, а затем

Если имя команды не найдено [другими словами, были только назначения переменных], назначения переменных влияют на текущую среду оболочки.

Это говорит мне о том, что присваивание переменных не является командами (и, следовательно, освобождается от отображения в ), как в других языках программирования. Это также объясняет, почему в выводе BASH_COMMANDнет строки , которая по сути является синтаксическим «маркером» для присваивания переменных.BASH_COMMAND=setsetset

С другой стороны, в последнем абзаце этого раздела говорится:

Если после расширения осталось имя команды, выполнение продолжается, как описано ниже. В противном случае,командавыходы.

... что говорит об обратном, и присвоение переменных тоже является командой.

решение3

Изобретательное использование $BASH_COMMAND

Недавно нашел этовпечатляющее использование $BASH_COMMANDпри реализации макроподобной функциональности.

Это основной трюк псевдонима, который заменяет использование ловушки DEBUG. Если вы прочитали часть предыдущего поста о ловушке DEBUG, вы узнаете переменную $BASH_COMMAND. В том посте я сказал, что она устанавливается в текст команды перед каждым вызовом ловушки DEBUG. Что ж, оказывается, она устанавливается перед выполнением каждой команды, ловушки DEBUG или нет (например, запустите 'echo “this command = $BASH_COMMAND”', чтобы понять, о чем я говорю). Назначая ей переменную (только для этой строки), мы захватываем BASH_COMMAND в самой внешней области действия команды, которая будет содержать всю команду.

Авторыпредыдущая статьятакже обеспечивает хорошую основу при реализации техники с использованием DEBUG trap. trapВ улучшенной версии устранено.

решение4

Наиболее распространенным применением trap...debug является улучшение заголовков, которые появляются в списке окон (^A") при использовании "screen".

Я пытался сделать «экран», «список окон» более удобными в использовании, поэтому начал находить статьи, в которых упоминаются «ловушка...отладка».

Я обнаружил, что метод, использующий PROMPT_COMMAND для отправки «нулевой последовательности заголовков», работает не так хорошо, поэтому я вернулся к методу trap...debug.

Вот как это сделать:

1 Сообщите «screen» о необходимости поиска escape-последовательности (включите ее), поместив «shelltitle '$|bash:'» в «$HOME/.screenrc».

2 Отключите распространение отладочной ловушки в под-оболочки. Это важно, потому что если она включена, все портится, используйте: "set +o functrace".

3 Отправьте escape-последовательность заголовка для "screen" для интерпретации: trap 'printf "\ek$(date +%Y%m%d%H%M%S) $(whoami)@$(hostname):$(pwd) ${BASH_COMMAND}\e\"' "DEBUG"

Это не идеально, но это помогает, и вы можете использовать этот метод, чтобы вставить в заголовок буквально все, что захотите.

См. следующий «список окон» (5 экранов):

Число Имя Флаги

1 bash:20161115232035 mcb@ken007:/home/mcb/ken007 RSYNCCMD="sudo rsync" myrsync.sh -R -r "${1}" "${BACKUPDIR}${2:+"/${2}"}" $ 2 bash:20161115230434 mcb@ken007:/home/mcb/ken007 ls --color=auto -la $ 3 bash:20161115230504 mcb@ken007:/home/mcb/ken007 cat bin/psg.sh $ 4 bash:20161115222415 mcb@ken007:/home/mcb/ken007 ssh ken009 $ 5 bash:20161115222450 mcb@ken007:/home/mcb/ken007 mycommoncleanup $

Связанный контент