什么是$ BASH_COMMAND变量适合?

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

当前正在执行或即将执行的命令,除非shell正在执行作为陷阱结果的命令,在这种情况下,它是在陷阱时执行的命令。

把那个陷阱角放在一边,如果我理解正确,这意味着当我执行命令时,变量BASH_COMMAND包含该命令。 在命令执行后是否未设置该变量并不是绝对清楚的(即,在命令运行时只能执行,但之后不能执行),尽管有人可能认为因为它是“ 当前正在执行或即将执行的命令” ,它不是执行的命令。

但是我们来看看:

 $ set | grep BASH_COMMAND= $ 

空。 我BASH_COMMAND='set | grep 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 | grep BASH_COMMAND= BASH_COMMAND='echo $BASH_COMMAND' $ 

等等。 它在我执行echo命令时设置的,之后并没有取消。 但是当我再次执行set时, BASH_COMMAND 没有设置为set命令。 无论我在这里执行set命令的频率如何,结果都保持不变。 那么,是在执行echoset变量,而不是在执行setset ? 让我们来看看。

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

什么? 所以当我执行echo $BASH_COMMAND时设置变量,但是当我执行echo Hello AskUbuntu 没有设置变量? 现在差异在哪里? 变量是否仅在当前命令本身实际强制shell评估变量时才设置? 让我们尝试不同的东西。 也许这次有一些外部命令,而不是内置的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 (当然,变量只扩展了一次),所以我grepped grep ,一次在$BASH_COMMAND文件中,不存在,两次in文件tmp

Q1:我目前的假设是否正确:

  • BASH_COMMAND仅在命令试图实际评估它时设置; 和
  • 执行命令后没有取消,即使描述可能会让我们相信这样吗?

Q2:如果是,为什么? 性能? 如果不是,那么上述命令序列中的行为怎么解释呢?

问题3:最后,是否存在实际有意义地使用此变量的任何情况? 我实际上是试图在$PROMPT_COMMAND使用它来分析正在执行的命令(并根据它执行一些操作),但我不能,因为只要在我的$PROMPT_COMMAND ,我执行一个命令来查看变量$BASH_COMMAND ,该变量获取该命令的设置。 即使我在MYVARIABLE=$BASH_COMMAND的开头做了MYVARIABLE=$BASH_COMMANDMYVARIABLE包含字符串MYVARIABLE=$BASH_COMMAND ,因为赋值也是一个命令。 (这个问题不是关于如何在$PROMPT_COMMAND执行中获取当前命令。我知道还有其他方法。)

这有点像海森堡的不确定性原理。 只要观察变量,我就改变它。

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

现在已经回答了Q3(正确地说,在我看来: BASH_COMMAND在陷阱中非常有用,而且几乎没有其他地方),让我们给Q1和Q2一个机会。

Q1的答案是:你的假设的正确性是不可判定的。 由于他们询问未指明的行为,因此无法确定这两个要点的真实性。 根据其规范, BASH_COMMAND的值在执行该命令期间设置为命令的文本。 规范没有说明在任何其他情况下它的值必须是什么,即没有执行命令时。 它可以有任何价值或根本没有价值。

Q2的答案“如果不是,那么上述命令序列中的行为怎么解释?” 然后逻辑地(如果有些迂腐):它是由BASH_COMMAND的值未定义的事实解释的。 由于它的值未定义,因此它可以具有任何值,这正是序列所显示的值。

后记

有一点,我认为你确实在规范中遇到了一个问题。 这就是你说的地方:

即使我在$ PROMPT_COMMAND的开头做了MYVARIABLE = $ BASH_COMMAND,MYVARIABLE也包含字符串MYVARIABLE = $ BASH_COMMAND, 因为赋值也是一个命令

我阅读bash手册页的方式,斜体字不是真的。 SIMPLE COMMAND EXPANSION部分解释了如何首先将命令行上的变量赋值放在一边,然后

如果没有命令名称结果[换句话说,只有变量赋值],则变量赋值会影响当前的shell环境。

这告诉我,变量赋值不是命令(因此不会出现在BASH_COMMAND ),就像在其他编程语言中一样。 这也可以解释为什么在BASH_COMMAND=set的输出中没有set BASH_COMMAND=set行, set为变量赋值的语法“标记”。

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

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

……这表示不然,变量赋值也是命令。

$ BASH_COMMAND的创造性用途

最近发现这个令人印象深刻的使用$ BASH_COMMAND来实现类似宏的function。

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

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

陷阱的一个看似常见的用途… debug用于改善当你使用“screen”时出现在windowlist(^ A“)中的标题。

我试图使“屏幕”,“窗口列表”更有用,所以我开始找到引用“陷阱…调试”的文章。

我发现使用PROMPT_COMMAND发送“空标题序列”的方法效果不好,所以我又回到了trap … debug方法。

这是你如何做到的:

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

2关闭调试陷阱到子shell的传播。 这很重要,因为如果它被启用,一切都会变得混乱,请使用:“set + o functrace”。

3发送“screen”的标题转义序列来解释:trap’printf“\ ek $(date +%Y%m%d%H%M%S)$(whoami)@ $(hostname):$(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 $