BASH:回显最后一个命令运行

Max*_*Max 75 bash command

我试图回应在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
Run Code Online (Sandbox Code Playgroud)

输出:

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]"]
Run Code Online (Sandbox Code Playgroud)

[问]有人可以帮我找到一种方法来回显最后一次运行命令,无论在bash脚本中调用此命令的方式/位置如何?

我的答案

尽管我的SO同事非常赞赏,但我选择编写一个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
Run Code Online (Sandbox Code Playgroud)

输出:

$ ./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
Run Code Online (Sandbox Code Playgroud)

我用多个参数测试了各种命令,bash变量作为参数,引用参数......并且run函数没有破坏它们.到目前为止我发现的唯一问题是运行一个中断的回声,但我不打算检查我的回声.

小智 153

Bash内置了一些功能来访问最后执行的命令.但这是最后一个完整的命令(例如整个case命令),而不是你最初请求的单个简单命令.

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

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

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

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

!! =上一个命令行

等等

所以,问题的最简单答案实际上是:

echo !!
Run Code Online (Sandbox Code Playgroud)

...或者:

echo "Last command run was ["!:0"] with arguments ["!:*"]"
Run Code Online (Sandbox Code Playgroud)

亲自尝试一下!

echo this is a test
echo !!
Run Code Online (Sandbox Code Playgroud)

在脚本中,默认情况下会关闭历史记录扩展,您需要启用它

set -o history -o histexpand
Run Code Online (Sandbox Code Playgroud)

  • 我见过的最有用的用例是[用sudo访问重新运行最后一个命令](http://xkcd.com/149/),即`sudo !!` (7认同)
  • 使用`set -o history -o histexpand; echo "!!"` 在 bash 脚本中我仍然收到错误消息:`!!: event not found`(没有引号也是一样。) (2认同)
  • 脚本中的`set -o history -o histexpand` -> 救星!谢谢! (2认同)
  • `!:-1` 不起作用。根据 bash 的文档,它不应该工作。可以使用 `!$` 来代替。请参阅 https://www.gnu.org/software/bash/manual/html_node/Word-Designators.html (2认同)

Gil*_*il' 55

命令历史记录是一种交互式功能.只在历史记录中输入完整的命令.例如,case当shell完成解析时,构造作为一个整体输入.无论是使用history内置!:p函数查找历史记录(也不是通过shell扩展()打印它)都可以执行您想要的操作,即打印简单命令的调用.

DEBUG陷阱使您可以在执行任何简单命令之前执行命令.要执行的命令的字符串版本(使用空格分隔的单词)在BASH_COMMAND变量中可用.

trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG
…
echo "last command is $previous_command"
Run Code Online (Sandbox Code Playgroud)

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

cmd=$previous_command ret=$?
if [ $ret -ne 0 ]; then echo "$cmd failed with error code $ret"; fi
Run Code Online (Sandbox Code Playgroud)

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

set -e
trap 'echo "exit $? due to $previous_command"' EXIT
Run Code Online (Sandbox Code Playgroud)

请注意,如果您正在尝试跟踪脚本以查看它正在执行的操作,请忘记所有这些并使用set -x.


Her*_*ium 15

看完后回答吉尔斯,我决定去看看,如果$BASH_COMMANDVAR也可以(和期望的值)的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"
Run Code Online (Sandbox Code Playgroud)

输出是

foo
Command [false 12345] exited with code [1]
Run Code Online (Sandbox Code Playgroud)

bar永远不会打印,因为set -e当命令失败并且false命令总是失败时(根据定义)会导致bash退出脚本.该12345传递给false在这里只用来表明该参数传递给失败的命令被捕获,以及(在false命令忽略传递给它的任何参数)

  • 这绝对是最好的解决方案。“set -euo Pipefail”对我来说就像一个魅力 (2认同)

Mar*_*ago 7

我能够通过set -x在主脚本中使用(使脚本打印出每个执行的命令)并编写一个包装脚本来实现这一点,该脚本只显示生成的最后一行输出set -x.

这是主要的脚本:

#!/bin/bash
set -x
echo some command here
echo last command
Run Code Online (Sandbox Code Playgroud)

这是包装脚本:

#!/bin/sh
./test.sh 2>&1 | grep '^\+' | tail -n 1 | sed -e 's/^\+ //'
Run Code Online (Sandbox Code Playgroud)

运行包装器脚本会将其生成为输出:

echo last command
Run Code Online (Sandbox Code Playgroud)


小智 6

history | tail -2 | head -1 | cut -c8-999

tail -2返回历史记录中的最后两个命令行 head -1仅返回第一行 cut -c8-999仅返回命令行,删除 PID 和空格。