Согласно руководству по Bash , переменная окружения 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
должен содержать grep
команду, grep
должен выполнить 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
, один раз в файле, $BASH_COMMAND
который не существует, и два раза в файле tmp
.
Q1: верно ли мое текущее предположение, что:
BASH_COMMAND
устанавливается только тогда, когда команда пытается его реально оценить; а также- оно не сбрасывается после выполнения команды, хотя описание может заставить нас поверить в это?
Q2: если да, то почему? Спектакль? Если нет, как еще можно объяснить поведение в приведенной выше последовательности команд?
Q3: Наконец, есть ли сценарий, в котором эта переменная могла бы реально использоваться осмысленно? Я на самом деле пытался использовать его внутри $PROMPT_COMMAND
для анализа выполняемой команды (и делать что-то в зависимости от этого), но я не могу, потому что, как только внутри $PROMPT_COMMAND
, я выполняю команду, чтобы посмотреть на переменную $BASH_COMMAND
, переменную получает наборы для этой команды. Даже когда я делаю MYVARIABLE=$BASH_COMMAND
прямо в начале моего $PROMPT_COMMAND
, тогда MYVARIABLE
содержит строку MYVARIABLE=$BASH_COMMAND
, потому что назначение тоже команда. (Этот вопрос не о том, как я могу получить текущую команду в ходе $PROMPT_COMMAND
выполнения. Я знаю, что есть и другие способы.)
Это немного похоже на принцип неопределенности Гейзенберга. Просто наблюдая за переменной, я меняю ее.
источник
bash
-гуру.Ответы:
Отвечая на третий вопрос: конечно, его можно использовать осмысленно, так как в руководстве по Bash четко намекает - в ловушку, например:
источник
$BASH_COMMAND
действительно может использоваться осмысленно даже в других контекстах, кроме ловушки. Тем не менее, использование, которое вы описываете, является, вероятно, наиболее типичным и простым. Итак, ваш ответ, а также ответ DocSalvager, ответьте лучше всего на мой Q3 , и это было самым важным для меня. Что касается Q1 и Q2, как указано @zwets, эти вещи не определены. Может случиться так, что$BASH_COMMAND
для доступа к нему дается только значение, если оно используется, или же некоторые странные внутренние условия программы могут привести к такому поведению. Кто знает?$BASH_COMMAND
устанавливать значение всегда, форсируя оценку$BASH_COMMAND
перед каждой командой. Для этого мы можем использовать ловушку DEBUG, которая выполняется перед каждой отдельной командой (все из них). Если мы выдадим, например,trap 'echo "$BASH_COMMAND" > /dev/null' DEBUG
то$BASH_COMMAND
, всегда будет оцениваться перед каждой командой (и присваивается значение этого$COMMAND
). Так что, если я выполню,set | grep BASH_COMMAND=
пока эта ловушка активна ... что ты знаешь? Теперь результат такойBASH_COMMAND=set
, как и ожидалось :)Теперь, когда на вопрос 3 ответили (правильно, на мой взгляд:
BASH_COMMAND
полезно в ловушках и вряд ли где-нибудь еще), давайте попробуем дать Q1 и Q2.Ответ на вопрос 1: правильность вашего предположения неразрешима. Истина ни одного из пунктов не может быть установлена, поскольку они спрашивают о неуказанном поведении. В соответствии с его спецификацией, значение
BASH_COMMAND
устанавливается в текст команды на время выполнения этой команды. В спецификации не указывается, каким должно быть ее значение в любой другой ситуации, то есть, когда команда не выполняется. Это может иметь любое значение или не иметь вообще.Ответ на вопрос 2 «Если нет, то как еще можно объяснить поведение в приведенной выше последовательности команд?» затем следует логически (если несколько педантично): это объясняется тем фактом, что значение
BASH_COMMAND
не определено. Поскольку его значение не определено, оно может иметь любое значение, которое в точности соответствует последовательности.постскриптум
Есть один момент, когда я думаю, что вы действительно попали в слабое место в спецификации. Это где вы говорите:
То, как я читаю справочную страницу bash, немного выделено курсивом. В этом разделе
SIMPLE COMMAND EXPANSION
объясняется, как сначала назначаются переменные в командной строке, а затемЭто наводит меня на мысль, что присвоение переменных не является командами (и, следовательно, освобождается от показа в них
BASH_COMMAND
), как и в других языках программирования. Это также объясняет, почемуBASH_COMMAND=set
на выходе нет строкиset
, поset
сути являющейся синтаксическим «маркером» для назначения переменных.OTOH, в последнем абзаце этого раздела говорится
... что говорит об обратном, а присвоение переменных тоже команды.
источник
set
BASH_COMMAND='set'
set
команды . Следовательно, согласно спецификациям это должно работать. Так что, реализация не совсем соответствует спецификациям или я неправильно понимаю спецификации? Поиск ответа на этот вопрос является своего рода целью Q1. +1 за постскриптум :)set
.set
это не «команда». Так же, как=
это не «команда». Обработка текста, следующего за этими токенами или окружающего его, может иметь совершенно иную логику, чем обработка «команды».set
это не считается «командой». Я бы не подумал, что$BASH_COMMAND
это окажется слишком неопределенным во многих обстоятельствах. Я думаю, по крайней мере, мы установили, чтоИзобретательское использование за $ BASH_COMMAND
Недавно было обнаружено впечатляющее использование $ BASH_COMMAND для реализации макроподобных функций.
Предыдущая статья автора также дает хороший опыт при реализации техники с использованием DEBUG
trap
.trap
Устраняется в улучшенной версии.источник
$BASH_COMMAND
может ли оно использоваться осмысленно в контексте, отличном от atrap
. И что это за польза! Используя его для реализации макрокоманд в bash - кто бы мог подумать, что это возможно? Спасибо за указатель.По-видимому, обычное использование отладки trap ... заключается в улучшении заголовков, которые появляются в списке окон (^ A "), когда вы используете" screen ".
Я пытался сделать "screen", "windowlist" более удобным, поэтому я начал находить статьи, ссылающиеся на "trap ... debug".
Я обнаружил, что метод, использующий PROMPT_COMMAND для отправки «последовательности нулевых заголовков», не работал так хорошо, поэтому я вернулся к методу trap ... debug.
Вот как вы это делаете:
1 Скажите «screen» найти escape-последовательность (включите ее), поместив «shelltitle» $ | bash: '«в« $ HOME / .screenrc ».
2 Отключите распространение отладочной ловушки на вложенные оболочки. Это важно, потому что, если он включен, все испорчено, используйте: "set + o functrace".
3 Отправьте escape-последовательность заголовка для «screen» для интерпретации: trap 'printf "\ ek $ (дата +% Y% m% d% H% M% S) $ (whoami) @ $ (имя хоста): $ (pwd) $ {BASH_COMMAND} \ e \ "'" DEBUG "
Это не идеально, но это помогает, и вы можете использовать этот метод, чтобы буквально поместить все, что вам нравится в заголовок
Смотрите следующий «список окон» (5 экранов):
Num Name Flags
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 $
источник