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作为参数,引用的参数…和run
function没有打破他们。 到目前为止我发现的唯一问题是运行一个回声打破,但我不打算检查我的回声无论如何。
命令历史是一个交互function。 只有完整的命令才能进入历史logging。 例如, case
结构作为一个整体input,当shell完成parsing时。 既不是通过内置的历史logging来查看历史history
(也不是通过shell扩展( !:p
)来打印它),你可能想要的是打印简单命令的调用。
DEBUG
陷阱允许您在任何简单命令执行之前执行命令。 在BASH_COMMAND
variables中可以使用要执行的命令的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命令总是失败(按定义)。 传递给false
的12345
就是在那里显示失败命令的参数也被捕获( 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可能没有得到任何积极的评价,我终于自己解决了我的问题。 这在初始文章中似乎是适当的。 🙂