$BASH_COMMAND 变量有什么用处?

$BASH_COMMAND 变量有什么用处?

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

当前正在执行或即将执行的命令,除非 shell 正在由于陷阱而执行命令,在这种情况下它是在陷阱时执行的命令。

撇开那个陷阱极端情况不谈,如果我理解正确的话,这意味着当我执行一个命令时,变量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?现在有什么区别?只有当前命令本身强制 shell 评估变量时,变量才会被设置吗?让我们尝试一些不同的东西。也许这次是一些外部命令,而不是 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_COMMANDtmp

问题 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

现在已经回答了 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=setsetset

另一方面,该部分的最后一段写道

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

...这表明情况并非如此,变量赋值也是命令。

答案3

$BASH_COMMAND 的创新用法

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

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

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

答案4

trap...debug 的一个常见用途是改进使用“screen”时窗口列表(^A)中出现的标题。

我试图让“屏幕”、“窗口列表”更易于使用,因此我开始寻找引用“陷阱...调试”的文章。

我发现使用 PROMPT_COMMAND 发送“空标题序列”的方法效果不太好,所以我恢复了陷阱...调试方法。

操作方法如下:

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 个屏幕):

编号 名称 标志

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 $

相关内容