回显 Bash 中运行的最后一个命令?
我试图回显 bash 脚本中运行的最后一个命令。我找到了一种使用一些 history,tail,head,sed
来完成此操作的方法,当命令从解析器的角度表示脚本中的特定行时,该方法可以正常工作。然而,在某些情况下,我没有得到预期的输出,例如当命令插入 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] 有人可以帮我找到一个回显最后一个运行命令的方法,无论该命令在 bash 脚本中如何/在何处
?
调用 run
函数 - 将其所有参数作为单个参数运行命令并在失败时显示命令及其错误代码 - 具有以下优点:
-我只需要用 run
添加要检查的命令,这样它们就可以保持在一行上,并且不会影响脚本的简洁性
-每当脚本在其中一个命令上失败时,脚本的最后一个输出行是一条消息,清楚地显示哪个命令失败及其退出代码,这使得调试更容易
示例脚本:
#!/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
我使用多个参数测试了各种命令、 bash 变量作为参数、带引号的参数...并且 run
函数没有破坏它们。到目前为止,我发现的唯一问题是运行回声会中断,但我不打算检查我的回声。
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:
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?
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
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
Bash 具有内置功能来访问最后执行的命令。但这是最后一个完整命令(例如整个
case
命令),而不是像您最初请求的单个简单命令。!:0
= 执行的命令的名称。!:1
= 上一个命令的第一个参数!:4
= 上一个命令的第四个参数!:*
= 所有上一个命令的参数!^
= 上一个命令的第一个参数(与!:1
相同)!$
= 的最后一个参数上一个命令!:-3
= 0-3 范围内的所有参数(含)!:2-5
= 2-5 范围内的所有参数(含)!!
= 上一个命令行等。
所以,这个问题的最简单答案事实上是:
...或者:
自己尝试一下!
在脚本中,历史记录扩展默认是关闭的,您需要启用它
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.!:0
= the name of command executed.!:1
= the first parameter of the previous command!:4
= the fourth parameter of the previous command!:*
= all of the parameters of the previous command!^
= the first parameter of the previous command (same as!:1
)!$
= the final parameter of the previous command!:-3
= all parameters in range 0-3 (inclusive)!:2-5
= all parameters in range 2-5 (inclusive)!!
= the previous command lineetc.
So, the simplest answer to the question is, in fact:
...alternatively:
Try it yourself!
In a script, history expansion is turned off by default, you need to enable it with
命令历史记录是一项交互式功能。只有完整的命令才会输入历史记录中。例如,当 shell 完成解析时,
case
构造会作为一个整体输入。既不使用内置的history
查找历史记录(也不通过 shell 扩展 (!:p
) 打印历史记录),也不会执行您似乎想要的操作,即打印调用的简单命令。DEBUG
陷阱 让您可以在执行任何简单命令之前执行命令。要执行的命令的字符串版本(单词之间用空格分隔)可在BASH_COMMAND
变量。请注意,每次运行命令时
previous_command
都会发生变化,因此将其保存到变量中以便使用它。如果您还想知道前一个命令的返回状态,请将两者保存在一个命令中。此外,如果您只想中止失败的命令,请使用 < code>set -e 使脚本在第一个失败的命令时退出。您可以显示
EXIT
陷阱。请注意,如果您尝试跟踪脚本以查看它在做什么,请忘记所有这些并使用
设置 -x
。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 thehistory
built-in (nor printing it through shell expansion (!:p
)) does what you seem to want, which is to print invocations of simple commands.The
DEBUG
trap lets 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 theBASH_COMMAND
variable.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.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 theEXIT
trap.Note that if you're trying to trace your script to see what it's doing, forget all this and use
set -x
.阅读了 Gilles 的答案后,我决定看看
$BASH_COMMAND
var 在EXIT
陷阱中是否也可用(以及所需的值) - 确实如此!因此,以下 bash 脚本按预期工作:
输出是
bar
永远不会打印,因为set -e
会导致 bash 在命令失败且错误命令始终时退出脚本失败(根据定义)。传递给false
的12345
只是为了表明失败命令的参数也被捕获(false
命令忽略传递的任何参数)到它)After reading the answer from Gilles, I decided to see if the
$BASH_COMMAND
var was also available (and the desired value) in anEXIT
trap - and it is!So, the following bash script works as expected:
The output is
bar
is never printed becauseset -e
causes bash to exit the script when a command fails and the false command always fails (by definition). The12345
passed tofalse
is just there to show that the arguments to the failed command are captured as well (thefalse
command ignores any arguments passed to it)我能够通过在主脚本中使用
set -x
(这使得脚本打印出执行的每个命令)并编写一个包装器脚本来实现这一点,该脚本仅显示由设置-x
。这是主脚本:
这是包装脚本:
运行包装脚本会产生以下输出:
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 byset -x
.This is the main script:
And this is the wrapper script:
Running the wrapper script produces this as output:
历史 |尾部-2 |头-1 | cut -c8-
tail -2
返回历史记录中的最后两个命令行head -1
仅返回第一行cut -c8-
仅返回命令行,删除 PID 和空格。history | tail -2 | head -1 | cut -c8-
tail -2
returns the last two command lines from historyhead -1
returns just first linecut -c8-
returns just command line, removing PID and spaces.最后一个命令 ($_) 和最后一个错误 ($?) 变量之间存在竞争条件。如果您尝试将其中之一存储在自己的变量中,则由于 set 命令,两者都已经遇到了新值。实际上,在这种情况下,最后一个命令根本没有任何价值。
这是我将(几乎)这两个信息存储在自己的变量中所做的操作,因此我的 bash 脚本可以确定是否有任何错误并使用上次运行命令设置标题:
此脚本将保留信息,如果发生错误并将获取最后一次运行命令。由于竞争条件,我无法存储实际值。此外,大多数命令实际上甚至不关心错误号,它们只是返回与“0”不同的东西。如果您使用 bash 的 errono 扩展,您会注意到这一点。
应该可以使用 bash 的“实习生”脚本之类的东西,就像在 bash 扩展中一样,但我不熟悉这样的东西,它也不会兼容。
更正
我认为不可能同时检索两个变量。虽然我喜欢代码的风格,但我认为它会被解释为两个命令。这是错误的,所以我的答案可以归结为:
虽然我的帖子可能不会得到任何积极的评价,但我最终自己解决了我的问题。
对于最初的帖子来说,这似乎是合适的。 :)
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.
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:
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.
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.
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:
Although my post might not get any positive rating, i solved my problem myself, finally.
And this seems appropriate regarding the intial post. :)