$ BASH_COMMAND变量有什么用?


25

根据Bash手册,环境变量BASH_COMMAND包含

当前正在执行或将要执行的命令,除非外壳程序由于陷阱而正在执行命令,在这种情况下,它是在陷阱发生时执行的命令。

撇开陷阱的情况,如果我理解正确,这意味着当我执行命令时,变量BASH_COMMAND包含该命令。这不是绝对清楚地表明变量是否是在执行命令后复位(也就是,只有avaialble 命令正在运行,但不是之后),尽管有人可能会认为,既然是“命令当前正在执行或即将执行的” ,不是刚刚执行的命令。

但是让我们检查一下:

$ 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'
$

嗯,好的...再次设置了变量。那么我目前的猜测正确吗?仅在需要评估变量时设置变量吗?为什么?为什么?出于性能原因?让我们再尝试一次。我们将尝试$BASH_COMMAND在文件中使用grep ,并且由于其中$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

问题1:我目前的假设是否正确:

  • BASH_COMMAND仅在命令尝试对其求值时设置;和
  • 即使描述可能使我们相信,在执行命令后仍未设置它?

问题2:如果是,为什么?性能?如果否,那么如何解释上述命令序列中的行为?

问题3:最后,在任何情况下都可以实际使用此变量吗?我实际上是在尝试使用它$PROMPT_COMMAND来分析正在执行的命令(并根据需要执行一些操作),但是我不能,因为在我的内部$PROMPT_COMMAND,我立即执行了一个命令来查看变量$BASH_COMMAND,即变量获取该命令的设置。即使我MYVARIABLE=$BASH_COMMAND刚开始在时$PROMPT_COMMAND,也会MYVARIABLE包含字符串MYVARIABLE=$BASH_COMMAND,因为赋值也是命令。(这个问题不关乎如何在$PROMPT_COMMAND执行中获取当前命令。我知道还有其他方法。)

这有点像海森堡的不确定性原理。只需观察变量,即可对其进行更改。


5
不错,但可能更适合unix.stackexchange.com ...那里有真正的bashüber- gurus
Rmano 2014年

好吧,可以将它移到那儿吗?Mods?
2014年

我真的不确定该怎么做---我想这是Mod特权。另一方面,我认为标记它并不明智-让我们看看是否有人在关闭标记上起作用。
Rmano 2014年

1
第三点,就是您要搜索的内容。也许根据该文章,您也可以找到前两点的答案。
RaduRădeanu2014年

2
+1使我感到困惑,但是阅读很有趣!

Answers:


15

回答第三个问题:当然,它可以按照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

啊,确实很好。因此,您是否会说陷阱是该变量有意义的唯一上下文?在手册中听起来像是一个极端案例:“正在执行的命令(...),除非外壳程序由于陷阱而正在执行命令, ...”
Malte Skoruppa 2014年

阅读@DocSalvager答案中链接的文章后,很明显,$BASH_COMMAND即使在陷阱以外的环境中,它们的确也可以有意义地使用。但是,您描述的用途可能是最典型且直接的。因此,您的答案以及DocSalvager的答案最能回答我的第三季度,这对我来说是最重要的。至于Q1和Q2,如@zwets所示,这些东西是不确定的。可能$BASH_COMMAND只是出于性能考虑,只有在访问时才给出一个值-否则某些奇怪的内部程序条件可能会导致该行为。谁知道?
Malte Skoruppa 2014年

1
在阿里纳斯,我发现,你可以强制$BASH_COMMAND应总是设定,通过强制的评估$BASH_COMMAND每个命令之前。为此,我们可以使用DEBUG陷阱,该陷阱在每个单个命令(全部为em)之前执行。如果我们发出,例如trap 'echo "$BASH_COMMAND" > /dev/null' DEBUG,然后$BASH_COMMAND总是会在每个命令之前计算(和分配的值$COMMAND)。因此,如果我set | grep BASH_COMMAND=在该陷阱处于活动状态时执行...您知道什么?现在的输出BASH_COMMAND=set符合预期:)
Malte Skoruppa 2014年

6

现在已经回答了Q3(我认为正确的BASH_COMMAND是:对陷阱有用,几乎在其他任何地方都没有用),现在让我们来看看Q1和Q2。

Q1的答案是:您的假设的正确性尚不确定。当它们要问未指明的行为时,这两个要点都不成立。根据其规范,BASH_COMMAND在执行命令的持续时间内,将的值设置为命令的文本。规范没有说明在任何其他情况下(即没有命令执行时)其值必须是什么。它可以具有任何值或根本没有任何值。

问题2的答案:“如果否,那么如何解释上述命令序列中的行为?” 然后从逻辑上讲(如果有些古怪的话):这是由于的值BASH_COMMAND未定义这一事实来解释的。由于其值是不确定的,因此它可以具有任何值,这正是序列显示的值。

后记

我认为您确实在规范中遇到了一个弱点。在这里你说:

即使当我在$ PROMPT_COMMAND的开始处立即执行MYVARIABLE = $ BASH_COMMAND时,MYVARIABLE也包含字符串MYVARIABLE = $ BASH_COMMAND,因为赋值也是命令

我阅读bash手册页的方式不是斜体。本节SIMPLE COMMAND EXPANSION说明如何首先保留命令行上的变量分配,然后再保留

如果没有命令名称结果(换句话说,只有变量分配),则变量分配会影响当前的shell环境。

对我来说,这与BASH_COMMAND其他编程语言一样,变量分配不是命令(因此免于显示)。然后,这也可以解释为什么BASH_COMMAND=set输出中没有一行setset本质上是变量分配的语法“标记”。

OTOH,在该部分的最后一段中说

如果扩展后还剩下命令名称,则执行如下所述。否则,命令退出。

…表示不正确,变量分配也是命令。


1
仅仅因为无法建立某些假设并不意味着它是不正确的。爱因斯坦认为,任何观察者(以任何速度)的光速与相对论的一个基本假设是相同的。无法建立,但这并不意味着它是不正确的。属性“可以建立”和“正确”是正交的。充其量您可以说“我们不知道它是否正确,因为它无法建立”。另外,它指定:它是执行期间的当前命令。因此,如果我键入内容,set我应该会BASH_COMMAND='set'在该输出中看到某处,这是
Malte Skoruppa 2014年

...但事实并非如此。即使那是set命令执行期间。因此,根据规格它应该工作。难道是实施不忠实地遵守规范,还是我误解了规范?找到该问题的答案是Q1的目的。为您的后记+1 :)
Malte Skoruppa

@MalteSkoruppa好点。相应地修正了答案,并添加了有关的评论set
zwets 2014年

1
在bash解释器中,set可能不是“命令”。就像=不是“命令”一样。在这些标记之后/周围的文本的处理可能与“命令”的处理具有完全不同的逻辑。
DocSalvager 2014年

你们两个人都很好。:)很有可能set不算作“命令”。$BASH_COMMAND在很多情况下,我都不会认为结果会被如此低估。我想至少我们已经确定手册页肯定可以更清楚了;)
Malte Skoruppa 2014年

2

$ BASH_COMMAND的创新用途

最近发现$ BASH_COMMAND在实现类似宏的功能中具有令人印象深刻的用法

这是别名的核心技巧,它取代了DEBUG陷阱的使用。如果您阅读上一篇文章中有关DEBUG陷阱的部分,您将认识到$ BASH_COMMAND变量。在那篇文章中,我说过它是在每次调用DEBUG陷阱之前设置为命令的文本。好吧,事实证明它是在执行每个命令之前设置的,调试陷阱还是不执行(例如,运行“ echo“此命令= $ BASH_COMMAND””以查看我在说什么)。通过为它分配一个变量(仅适用于该行),我们在命令的最外部范围捕获了BASH_COMMAND,它将包含整个命令。

作者的上一篇文章在使用DEBUG实现技术时也提供了一些良好的背景知识trap。将trap在改进版本消除。


+1我终于四处阅读了这两篇文章。哇!真读!很有启发性。那家伙是个bash 大师!荣誉!这也回答了我对德米特里(Dmitry)的答案的评论,即答案是否$BASH_COMMAND可以在除之外的其他上下文中有意义地使用trap。这是什么用途!用它在bash中实现宏命令-谁会想到呢?感谢您的指导。
Malte Skoruppa 2014年

0

trap ... debug的一种看似普通用法是改善使用“屏幕”时出现在窗口列表(^ A“)中的标题。

我试图使“屏幕”,“窗口列表”更有用,所以我开始查找引用“ trap ... debug”的文章。

我发现使用PROMPT_COMMAND发送“空标题序列”的方法效果不佳,因此我恢复为trap ... debug方法。

这是您的操作方式:

1通过将“ shelltitle'$ | bash:'”放入“ $ HOME / .screenrc”,告诉“ screen”寻找转义序列(启用它)。

2关闭调试陷阱到子外壳的传播。这很重要,因为如果启用它,那么一切都会变得糟透了,请使用:“ set + o functrace”。

3发送标题转义序列以供“屏幕”解释: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 $

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.