BASH:回应最后的命令运行

我正在试图回应在bash脚本中运行的最后一个命令。 我find了一个方法来处理一些history,tail,head,sed ,当从命令行parsing器的angular度来看,我的脚本中的某个特定行代表了一个特定的行。 但是,在某些情况下,我不会得到预期的输出,例如,当命令插入到case语句中时:

剧本:

 #!/bin/bash set -o history date last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //') echo "last command is [$last]" case "1" in "1") date last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //') echo "last command is [$last]" ;; esac 

输出:

 Tue May 24 12:36:04 CEST 2011 last command is [date] Tue May 24 12:36:04 CEST 2011 last command is [echo "last command is [$last]"] 

[Q]有人可以帮助我find一种方法来回应最后一个运行命令,无论在bash脚本中如何调用此命令?

我的答案

尽pipe我的同事们对此表示赞赏,但我select编写一个run函数 – 将所有参数作为单个命令运行,并在失败时显示命令及其错误代码 – 具有以下优点:
– 我只需要预先安排我要检查的命令,使它们保持在一行,而不会影响脚本的简洁性
– 当脚本在其中一个命令失败时,脚本的最后一个输出行是一条消息,清楚地显示哪个命令与其退出代码一起失败,这使得debugging变得更容易

示例脚本:

 #!/bin/bash die() { echo >&2 -e "\nERROR: $@\n"; exit 1; } run() { "$@"; code=$?; [ $code -ne 0 ] && die "command [$*] failed with error code $code"; } case "1" in "1") run ls /opt run ls /wrong-dir ;; esac 

输出:

 $ ./test.sh apacheds google iptables ls: cannot access /wrong-dir: No such file or directory ERROR: command [ls /wrong-dir] failed with error code 2 

我用多个参数testing各种命令,bashvariables作为参数,引用的参数…和runfunction没有打破他们。 到目前为止我发现的唯一问题是运行一个回声打破,但我不打算检查我的回声无论如何。

命令历史是一个交互function。 只有完整的命令才能进入历史logging。 例如, case结构作为一个整体input,当shell完成parsing时。 既不是通过内置的历史logging来查看历史history (也不是通过shell扩展( !:p )来打印它),你可能想要的是打印简单命令的调用。

DEBUG陷阱允许您在任何简单命令执行之前执行命令。 在BASH_COMMANDvariables中可以使用要执行的命令的string版本(用空格分隔单词)。

 trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG … echo "last command is $previous_command" 

请注意,每次运行命令时, previous_command都会更改,因此请将其保存到variables中以便使用它。 如果您还想知道前一个命令的返回状态,请将它们保存在一个命令中。

 cmd=$previous_command ret=$? if [ $ret -ne 0 ]; then echo "$cmd failed with error code $ret"; fi 

此外,如果您只想中止失败的命令,请使用set -e使您的脚本在第一个失败的命令上退出。 您可以显示来自EXIT陷阱的最后一个命令。

 set -e trap 'echo "exit $? due to $previous_command"' EXIT 

请注意,如果你想跟踪你的脚本来看看它在做什么,忘记所有这些,并使用set -x

Bash内置了访问执行的最后一个命令的function。 但是这是最后一个完整的命令(例如整个命令的命令),而不是像你最初所要求的单个简单的命令。

!:0 =执行的命令名称。

!:1 =上一个命令的第一个参数

!:* =上一个命令的所有参数

!:-1 =上一个命令的最后一个参数

!! =上一个命令行

等等

所以,最简单的回答就是:

 echo !! 

…或者:

 echo "Last command run was ["!:0"] with arguments ["!:*"]" 

自己试试吧!

 echo this is a test echo !! 

在脚本中,历史扩展默认是closures的,您需要启用它

 set -o history -o histexpand 

在阅读了Gilles的回答之后,我决定看看$BASH_COMMAND var是否在EXIT陷阱中可用(和所需的值) – 这是!

所以,下面的bash脚本按预期工作:

 #!/bin/bash exit_trap () { local lc="$BASH_COMMAND" rc=$? echo "Command [$lc] exited with code [$rc]" } trap exit_trap EXIT set -e echo "foo" false 12345 echo "bar" 

输出是

 foo Command [false 12345] exited with code [1] 

bar从不打印,因为set -e会导致bash在命令失败时退出脚本,并且false命令总是失败(按定义)。 传递给false12345就是在那里显示失败命令的参数也被捕获( false命令忽略了传递给它的任何参数)

我可以通过在主脚本中使用set -x (它使脚本输出每个执行的命令)并编写一个只显示set -x生成的输出的最后一行的包装脚本来实现。

这是主要的脚本:

 #!/bin/bash set -x echo some command here echo last command 

这是包装脚本:

 #!/bin/sh ./test.sh 2>&1 | grep '^\+' | tail -n 1 | sed -e 's/^\+ //' 

运行包装脚本产生这个输出:

 echo last command 

最后一个命令($ _)和最后一个错误($?)variables之间有一个竞争条件。 如果您尝试将其中一个存储在自己的variables中,则由于set命令,两者都会遇到新的值。 其实最后的命令在这种情况下根本没有任何价值。

下面是我所做的将两个信息存储在自己的variables中,所以我的bash脚本可以确定是否有任何错误,并设置最后一个运行命令的标题:

  # This construct is needed, because of a racecondition when trying to obtain # both of last command and error. With this the information of last error is # implied by the corresponding case while command is retrieved. if [[ "${?}" == 0 && "${_}" != "" ]] ; then # Last command MUST be retrieved first. LASTCOMMAND="${_}" ; RETURNSTATUS='✓' ; elif [[ "${?}" == 0 && "${_}" == "" ]] ; then LASTCOMMAND='unknown' ; RETURNSTATUS='✓' ; elif [[ "${?}" != 0 && "${_}" != "" ]] ; then # Last command MUST be retrieved first. LASTCOMMAND="${_}" ; RETURNSTATUS='✗' ; # Fixme: "$?" not changing state until command executed. elif [[ "${?}" != 0 && "${_}" == "" ]] ; then LASTCOMMAND='unknown' ; RETURNSTATUS='✗' ; # Fixme: "$?" not changing state until command executed. fi 

这个脚本将保留这些信息,如果发生错误并将获得最后的运行命令。 由于竞争条件,我不能存储实际价值。 此外,大多数命令实际上甚至不关心错误代码,它们只是返回与“0”不同的东西。 你会注意到,如果你使用bash的errono扩展。

应该可以用像bash这样的“intern”脚本来实现,但是我不熟悉类似的东西,它也不兼容。

更正

我没有想到,有可能同时检索这两个variables。 虽然我喜欢代码的风格,但我认为它会被解释为两个命令。 这是错误的,所以我的答案归结为:

  # Because of a racecondition, both MUST be retrieved at the same time. declare RETURNSTATUS="${?}" LASTCOMMAND="${_}" ; if [[ "${RETURNSTATUS}" == 0 ]] ; then declare RETURNSYMBOL='✓' ; else declare RETURNSYMBOL='✗' ; fi 

虽然我的post可能没有得到任何积极的评价,我终于自己解决了我的问题。 这在初始文章中似乎是适当的。 🙂