![Для чего нужна переменная $BASH_COMMAND?](https://rvso.com/image/1047576/%D0%94%D0%BB%D1%8F%20%D1%87%D0%B5%D0%B3%D0%BE%20%D0%BD%D1%83%D0%B6%D0%BD%D0%B0%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F%20%24BASH_COMMAND%3F.png)
СогласноРуководство по Башу, переменная окружения 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_COMMAND
should затем содержит grep
команду, grep
should выполнить 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=set
set
set
С другой стороны, в последнем абзаце этого раздела говорится:
Если после расширения осталось имя команды, выполнение продолжается, как описано ниже. В противном случае,командавыходы.
... что говорит об обратном, и присвоение переменных тоже является командой.
решение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 $