回显在 Bash 中运行的最后一个命令?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6109225/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Echoing the last command run in Bash?
提问by Max
I am trying to echo the last command run inside a bash script. I found a way to do it with some history,tail,head,sed
which works fine when commands represent a specific line in my script from a parser standpoint. However under some circumstances I don't get the expected output, for instance when the command is inserted inside a case
statement:
我正在尝试回显在 bash 脚本中运行的最后一个命令。我找到了一种方法,history,tail,head,sed
当命令从解析器的角度代表我的脚本中的特定行时,它可以很好地工作。但是在某些情况下,我没有得到预期的输出,例如当命令插入到case
语句中时:
The script:
剧本:
#!/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
The output:
输出:
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] Can someone help me find a way to echo the last run command regardless of how/where this command is called within the bash script?
[问] 有人能帮我找到一种方法来回显上次运行命令,而不管在 bash 脚本中如何/在何处调用此命令?
My answer
我的答案
Despite the much appreciated contributions from my fellow SO'ers, I opted for writing a run
function - which runs all its parameters as a single command and display the command and its error code when it fails - with the following benefits:
-I only need to prepend the commands I want to check with run
which keeps them on one line and doesn't affect the conciseness of my script
-Whenever the script fails on one of these commands, the last output line of my script is a message that clearly displays which command fails along with its exit code, which makes debugging easier
尽管我的 SO'ers 同事非常感谢他们的贡献,我还是选择编写一个run
函数——它将所有参数作为单个命令运行,并在失败时显示命令及其错误代码——具有以下好处:
-我只需要将我想检查的命令run
放在一行前面,并且不会影响我的脚本的简洁性 -
每当脚本在这些命令之一上失败时,我的脚本的最后输出行是一条消息,清楚地显示哪个命令失败及其退出代码,这使得调试更容易
Example script:
示例脚本:
#!/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
The output:
输出:
$ ./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
I tested various commands with multiple arguments, bash variables as arguments, quoted arguments... and the run
function didn't break them. The only issue I found so far is to run an echo which breaks but I do not plan to check my echos anyway.
我测试了带有多个参数的各种命令,bash 变量作为参数,引用参数......并且该run
函数没有破坏它们。到目前为止我发现的唯一问题是运行一个中断的回声,但我不打算检查我的回声。
采纳答案by Gilles 'SO- stop being evil'
The command history is an interactive feature. Only complete commands are entered in the history. For example, the case
construct is entered as a whole, when the shell has finished parsing it. Neither looking up the history with the history
built-in (nor printing it through shell expansion (!:p
)) does what you seem to want, which is to print invocations of simple commands.
命令历史是一种交互式功能。历史中只输入完整的命令。例如,case
当 shell 完成对构造的解析时,构造作为一个整体输入。使用history
内置!:p
函数查找历史记录(也不通过 shell 扩展 ( )打印它)都不会做您似乎想要的,即打印简单命令的调用。
The DEBUG
traplets you execute a command right before any simple command execution. A string version of the command to execute (with words separated by spaces) is available in the BASH_COMMAND
variable.
该DEBUG
陷阱让你任何简单的命令执行前执行命令权。要执行的命令的字符串版本(单词用空格分隔)在BASH_COMMAND
变量中可用。
trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG
…
echo "last command is $previous_command"
Note that previous_command
will change every time you run a command, so save it to a variable in order to use it. If you want to know the previous command's return status as well, save both in a single command.
请注意,previous_command
每次运行命令时都会更改,因此将其保存到变量中以便使用。如果您还想知道上一个命令的返回状态,请将两者保存在一个命令中。
cmd=$previous_command ret=$?
if [ $ret -ne 0 ]; then echo "$cmd failed with error code $ret"; fi
Furthermore, if you only want to abort on a failed commands, use set -e
to make your script exit on the first failed command. You can display the last command from the EXIT
trap.
此外,如果您只想在失败的命令上中止,请使用set -e
使您的脚本在第一个失败的命令上退出。您可以显示来自EXIT
陷阱的最后一个命令。
set -e
trap 'echo "exit $? due to $previous_command"' EXIT
Note that if you're trying to trace your script to see what it's doing, forget all this and use set -x
.
请注意,如果您试图跟踪脚本以查看它在做什么,请忘记所有这些并使用set -x
.
回答by groovyspaceman
Bash has built in features to access the last command executed. But that's the last whole command (e.g. the whole case
command), not individual simple commands like you originally requested.
Bash 内置了访问最后执行的命令的功能。但这是最后一个完整的命令(例如整个case
命令),而不是您最初请求的单个简单命令。
!:0
= the name of command executed.
!:0
= 执行的命令的名称。
!:1
= the first parameter of the previous command
!:1
= 上一条命令的第一个参数
!:*
= all of the parameters of the previous command
!:*
= 上一条命令的所有参数
!:-1
= the final parameter of the previous command
!:-1
= 上一条命令的最后一个参数
!!
= the previous command line
!!
= 上一个命令行
etc.
等等。
So, the simplest answer to the question is, in fact:
所以,这个问题最简单的答案实际上是:
echo !!
...alternatively:
...或者:
echo "Last command run was ["!:0"] with arguments ["!:*"]"
Try it yourself!
自己试试吧!
echo this is a test
echo !!
In a script, history expansion is turned off by default, you need to enable it with
在脚本中,历史扩展默认是关闭的,你需要用
set -o history -o histexpand
回答by Hercynium
After reading the answerfrom Gilles, I decided to see if the $BASH_COMMAND
var was also available (and the desired value) in an EXIT
trap - and it is!
在阅读了Gilles的答案后,我决定看看var 在陷阱中是否也可用(以及所需的值)- 确实如此!$BASH_COMMAND
EXIT
So, the following bash script works as expected:
因此,以下 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"
The output is
输出是
foo
Command [false 12345] exited with code [1]
bar
is never printed because set -e
causes bash to exit the script when a command fails and the false command always fails (by definition). The 12345
passed to false
is just there to show that the arguments to the failed command are captured as well (the false
command ignores any arguments passed to it)
bar
永远不会打印,因为set -e
当命令失败时会导致 bash 退出脚本并且 false 命令总是失败(根据定义)。该12345
传递给false
在这里只用来表明该参数传递给失败的命令被捕获,以及(在false
命令忽略传递给它的任何参数)
回答by Mark Drago
I was able to achieve this by using set -x
in the main script (which makes the script print out every command that is executed) and writing a wrapper script which just shows the last line of output generated by set -x
.
我能够通过set -x
在主脚本中使用(它使脚本打印出执行的每个命令)并编写一个包装脚本来实现这一点,该脚本仅显示set -x
.
This is the main script:
这是主要脚本:
#!/bin/bash
set -x
echo some command here
echo last command
And this is the wrapper script:
这是包装脚本:
#!/bin/sh
./test.sh 2>&1 | grep '^\+' | tail -n 1 | sed -e 's/^\+ //'
Running the wrapper script produces this as output:
运行包装器脚本会产生以下输出:
echo last command
回答by Guma
history | tail -2 | head -1 | cut -c8-999
history | tail -2 | head -1 | cut -c8-999
tail -2
returns the last two command lines from history
head -1
returns just first line
cut -c8-999
returns just command line, removing PID and spaces.
tail -2
返回历史记录中的最后两个命令行
head -1
仅返回第一行
cut -c8-999
仅返回命令行,删除 PID 和空格。
回答by WGRM
There is a racecondition between the last command ($_) and last error ( $?) variables. If you try to store one of them in an own variable, both encountered new values already because of the set command. Actually, last command hasn't got any value at all in this case.
最后一个命令 ($_) 和最后一个错误 ($?) 变量之间存在竞争条件。如果您尝试将其中之一存储在自己的变量中,由于 set 命令,两者都已经遇到了新值。实际上,在这种情况下,最后一个命令根本没有任何价值。
Here is what i did to store (nearly) both informations in own variables, so my bash script can determine if there was any error AND setting the title with the last run command:
这是我将(几乎)这两个信息存储在自己的变量中的操作,因此我的 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
This script will retain the information, if an error occured and will obtain the last run command. Because of the racecondition i can not store the actual value. Besides, most commands actually don't even care for error noumbers, they just return something different from '0'. You'll notice that, if you use the errono extention of bash.
如果发生错误,此脚本将保留该信息并获取上次运行命令。由于竞争条件,我无法存储实际值。此外,大多数命令实际上甚至不关心错误编号,它们只是返回与“0”不同的东西。你会注意到,如果你使用 bash 的错误扩展。
It should be possible with something like a "intern" script for bash, like in bash extention, but i'm not familiar with something like that and it wouldn't be compatible as well.
应该可以使用诸如 bash 的“实习生”脚本之类的东西,例如 bash 扩展,但我不熟悉类似的东西,而且也不兼容。
CORRECTION
更正
I didn't think, that it was possible to retrieve both variables at the same time. Although i like the style of the code, i assumed it would be interpreted as two commands. This was wrong, so my answer devides down to:
我不认为可以同时检索两个变量。虽然我喜欢代码的风格,但我认为它会被解释为两个命令。这是错误的,所以我的答案归结为:
# 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
Although my post might not get any positive rating, i solved my problem myself, finally. And this seems appropriate regarding the intial post. :)
虽然我的帖子可能没有得到任何正面评价,但我终于自己解决了我的问题。这对于初始帖子似乎是合适的。:)