bash – 自动捕获最后执行的命令的输出到一个variables中

我希望能够在后续的命令中使用最后执行的命令的结果。 例如,

$ find . -name foo.txt ./home/user/some/directory/foo.txt 

现在让我们说,我想能够在编辑器中打开文件,或删除它,或者做一些其他的事情,例如

 mv <some-variable-that-contains-the-result> /some/new/location 

我该怎么做? 也许使用一些bashvariables?

更新:

为了澄清,我不想手动分配的东西。 我所追求的就像内置的bashvariables,例如

 ls /tmp cd $_ 

$_保存了前一个命令的最后一个参数。 我想要类似的东西,但最后一个命令的输出。

最终更新:

塞思的回答工作得很好。 记住几件事情:

  • 当第一次尝试解决scheme时,请不要忘记touch /tmp/x
  • 只有当最后一个命令的退出代码成功时才会存​​储结果

这是一个非常糟糕的解决scheme,但似乎大部分时间都在工作。 在testing过程中,我注意到在命令行上获取^ C时,它有时效果不佳,尽pipe我稍微调整了一下以performance得更好一些。

这种黑客攻击只是一种交互模式,我相当自信,我不会推荐给任何人。 后台命令可能会导致比正常情况下更less的定义行为。 其他答案是以编程方式获得结果的更好方法。


这就是说,这里是“解决scheme”:

 PROMPT_COMMAND='LAST="`cat /tmp/x`"; exec >/dev/tty; exec > >(tee /tmp/x)' 

设置这个bash环境variables并根据需要发出命令。 $LAST通常会有你正在寻找的输出:

 startide seth> fortune Courtship to marriage, as a very witty prologue to a very dull play. -- William Congreve startide seth> echo "$LAST" Courtship to marriage, as a very witty prologue to a very dull play. -- William Congreve 

我不知道任何自动执行此操作的variables。 除了复制粘贴结果之外,你可以重新运行你刚刚做的任何事情,例如

 vim $(!!) 

哪里!! 是历史扩张,意思是“以前的命令”。

如果你希望有一个空格或其他字符的文件名,可能会阻止正确的参数parsing,请引用结果( vim "$(!!)" )。 如果不加引号,只要不包含空格或其他shellparsing令牌,就可以一次打开多个文件。

Bash是一种丑陋的语言。 是的,您可以将输出分配给variables

 MY_VAR="$(find -name foo.txt)" echo "$MY_VAR" 

但希望你最难find只返回一个结果,并且结果中没有任何“奇怪的”字符,如回车或换行符,因为它们将被分配给Bashvariables时被无声修改。

但是更好地使用它时要小心地引用你的variables!

最好直接对文件进行操作,例如使用find-execdir (参考手册)。

 find -name foo.txt -execdir vim '{}' ';' 

要么

 find -name foo.txt -execdir rename 's/\.txt$/.xml/' '{}' ';' 

有多种方法可以做到这一点。 一种方法是使用v=$(command)将命令的输出分配给v 。 例如:

 v=$(date) echo $v 

你也可以使用反引号。

 v=`date` echo $v 

从Bash初学者指南 ,

当使用旧式反向引用的replaceforms时,除了后面跟有“$”,“`”或“\”之外,反斜杠将保留其字面含义。 第一个反斜杠前面没有反斜杠会终止命令replace。 当使用“$(COMMAND)”forms时,括号之间的所有字符组成命令; 没有一个是专门处理的

编辑:在问题的编辑后,似乎这不是OP所寻找的东西。 据我所知,最后一个命令的输出没有像$_这样的特殊variables。

这很容易。 使用反引号:

 var=`find . -name foo.txt` 

然后你可以在将来的任何时候使用它

 echo $var mv $var /somewhere 

我想你可能能够破解一个解决scheme,涉及到设置你的shell脚本包含:

 #!/bin/sh bash | tee /var/log/bash.out.log 

那么如果你设置$PROMPT_COMMAND来输出一个分隔符,你可以编写一个辅助函数(也许称为_ )来获取这个日志的最后一个块,所以你可以像这样使用它:

 % find lots*of*files ... % echo "$(_)" ... # same output, but doesn't run the command again 

Disclamers:

  • 答案是半年后的:D
  • 我是一个沉重的tmux用户
  • 你必须在tmux中运行你的shell才能工作

在tmux中运行交互式shell时,可以轻松访问terminal当前显示的数据。 让我们来看看一些有趣的命令:

  • tmux capture-pane :这个复制显示的数据到一个tmux的内部缓冲区。 它可以复制当前不可见的历史logging,但现在我们对此不感兴趣
  • tmux list-buffers :显示关于捕获的缓冲区的信息。 最新的将有数字0。
  • tmux show-buffer -b(缓冲区号) :打印terminal上给定缓冲区的内容
  • tmux粘贴缓冲区-b(缓冲区号) :这将粘贴给定缓冲区的内容作为input

是的,这给了我们很多可能:)现在,我设置了一个简单的别名: alias L="tmux capture-pane; tmux showb -b 0 | tail -n 3 | head -n 1"现在每次我需要访问最后一行时,只需使用$(L)即可获得它。

这与程序使用的输出stream(stdin或stderr),打印方法(ncurses等)以及程序的退出代码无关 – 只需要显示数据。

你可以在你的bashconfiguration文件中设置以下别名:

 alias s='it=$($(history | tail -2 | head -1 | cut -d" " -f4-))' 

然后,通过在任意命令后input“s”,可以将结果保存到一个shellvariables“it”中。

所以示例用法是:

 $ which python /usr/bin/python $ s $ file $it /usr/bin/python: symbolic link to `python2.6' 

我只是从这里的build议中提炼出这个bash函数:

 grab() { grab=$("$@") echo $grab } 

那么,你只是做:

 > grab date Do 16. Feb 13:05:04 CET 2012 > echo $grab Do 16. Feb 13:05:04 CET 2012 

更新 :一个匿名用户build议用printf '%s\n'来代替echo ,这个优点是它不会处理抓取文本中的-e选项。 所以,如果你期望或遇到这样的特点,请考虑这个build议。 另一个select是使用cat <<<$grab代替。

我通常做这里的其他人所build议的……没有作业:

 $find . -iname '*.cpp' -print ./foo.cpp ./bar.cpp $vi `!!` 2 files to edit 

如果你喜欢,你可以更喜欢:

 $grep -R "some variable" * | grep -v tags ./foo/bar/xxx ./bar/foo/yyy $vi `!!` 

通过说“我希望能够在后续命令中使用最后执行的命令的结果”, 我假设 – 你的意思是任何命令的结果,而不仅仅是查找。

如果是这样的话 – xargs是你正在寻找的。

find . -name foo.txt -print0 | xargs -0 -I{} mv {} /some/new/location/{}

或者如果您有兴趣首先看到输出:

find . -name foo.txt -print0

!! | xargs -0 -I{} mv {} /some/new/location/{}

这个命令处理多个文件,就像一个魅力,即使path和/或文件名包含空间(S)。

注意命令的mv {} / some / new / location / {}部分。 这个命令是为早期命令打印的每一行构build和执行的。 这里由前面的命令打印的行被replace为{}

摘自xargs手册页:

xargs – 从标准input构build和执行命令行

有关更多详细信息,请参见手册页: man xargs

用反引号捕获输出:

 output=`program arguments` echo $output emacs $output 

如果你想要的是重新运行你的最后一个命令并获得输出,一个简单的bashvariables可以工作:

 LAST=`!!` 

那么你可以在输出上运行你的命令:

 yourCommand $LAST 

这将产生一个新的过程,并重新运行你的命令,然后给你输出。 这听起来像你真正想要的是命令输出的bash历史文件。 这意味着您将需要捕获bash发送到您的terminal的输出。 你可以写一些东西来看/ dev或/ proc必要的,但是这很麻烦。 你也可以在你的术语和bash之间创build一个“特殊pipe道”,在中间的一个tee命令中redirect到你的输出文件。

但是这两种都是一种很好的解决办法。 我认为最好的办法就是终结者 ,这是一个更加现代化的输出loggingterminal。 只要检查你的日志文件的最后一个命令的结果。 一个类似于上面的bashvariables会使这个变得更简单。

执行完命令之后,有一种方法可以实现,并决定将结果存储在一个variables中:

 $ find . -name foo.txt ./home/user/some/directory/foo.txt $ OUTPUT=`!!` $ echo $OUTPUT ./home/user/some/directory/foo.txt $ mv $OUTPUT somewhere/else/ 

或者,如果您事先知道要将结果存入variables,则可以使用反引号:

 $ OUTPUT=`find . -name foo.txt` $ echo $OUTPUT ./home/user/some/directory/foo.txt 

作为现有答案的替代方法:如果文件名可以包含如下所示的空格,请使用:

 find . -name foo.txt | while IFS= read -r var; do echo "$var" done 

正如我写的,只有当你不得不在文件名中预期空白的时候,区别才是相关的。

注意:唯一的内置东西不是关于输出,而是关于最后一个命令的状态。

如何将export $last=$(!!)到.bashrc或.profile文件?

编辑:没关系做各种愚蠢的事情。

编辑#2:

  1. 下载bash源代码
  2. 学习CPP
  3. 代码在新的魔术variables
  4. 利润?

你可以使用!!:1。 例:

 ~]$ ls *.~ class1.cpp~ class1.h~ main.cpp~ CMakeList.txt~ ~]$ rm !!:1 rm class1.cpp~ class1.h~ main.cpp~ CMakeList.txt~ ~]$ ls file_to_remove1 file_to_remove2 file_to_remove1 file_to_remove2 ~]$ rm !!:1 rm file_to_remove1 ~]$ rm !!:2 rm file_to_remove2 

我有一个类似的需求,我想用最后一个命令的输出到下一个。 很像| | (pipe)。 例如

 $ which gradle /usr/bin/gradle $ ls -alrt /usr/bin/gradle 

到像 –

 $ which gradle |: ls -altr {} 

解决scheme:创build此自定义pipe道。 真的很简单,用xargs –

 $ alias :='xargs -I{}' 

基本上没有什么能为xargs创造一个短手,它像魅力一样工作,而且非常方便。 我只是在.bash_profile文件中添加别名。

这不是严格的bash解决scheme,但是您可以使用sed的pipe道获取前一个命令输出的最后一行。

首先让我们看看我在文件夹“A”

 rasjani@helruo-dhcp022206::~$ find a a a/foo a/bar a/bat a/baz rasjani@helruo-dhcp022206::~$ 

然后,你用ls和cd的例子会变成sed&piping变成这样的东西:

 rasjani@helruo-dhcp022206::~$ cd `find a |sed '$!d'` rasjani@helruo-dhcp022206::~/a/baz$ pwd /home/rasjani/a/baz rasjani@helruo-dhcp022206::~/a/baz$ 

所以,真正的魔法发生在sed上,你将任何曾经input的命令input到sed中,sed打印最后一行,你可以使用back ticks作为参数。 或者你也可以把它和xargs结合起来。 (shell中的“man xargs”是你的朋友)

shell没有perl类似的特殊符号来存储最后一个命令的回显结果。

学习用awk使用pipe道符号。

 find . | awk '{ print "FILE:" $0 }' 

在上面的例子中,你可以这样做:

 find . -name "foo.txt" | awk '{ print "mv "$0" ~/bar/" | "sh" }' 
 find . -name foo.txt 1> tmpfile && mv `cat tmpfile` /path/to/some/dir/ 

尽pipe是肮脏的,但还是另一种方式。