使用 Bash 自动将最后一个命令的输出捕获到变量中?

2024-10-12 09:57:00
admin
原创
85
摘要:问题描述:我希望能够在后续命令中使用最后执行的命令的结果。例如,$ find . -name foo.txt ./home/user/some/directory/foo.txt 现在假设我希望能够在编辑器中打开该文件,或者删除它,或者用它做其他事情,例如mv <some-variable-th...

问题描述:

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

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

现在假设我希望能够在编辑器中打开该文件,或者删除它,或者用它做其他事情,例如

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

我该怎么做?也许使用一些 bash 变量?

更新:

澄清一下,我不想手动分配东西。我想要的是内置 bash 变量之类的东西,例如

ls /tmp
cd $_

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

最后更新:

Seth 的答案非常有效。需要记住以下几点:

  • touch /tmp/x第一次尝试解决方案时不要忘记

  • 仅当最后一个命令的退出代码成功时才会存​​储结果


解决方案 1:

我不知道有任何变量可以自动执行此操作。除了复制粘贴结果之外,您还可以重新运行刚刚执行的操作,例如

vim $(!!)

历史扩展的意思是!!‘前一个命令’。

如果您预计单个文件名中可能包含空格或其他字符,而这些字符可能会妨碍正确的参数解析,请将结果括起来 ( vim "$(!!)")。如果不将其括起来,则可以一次打开多个文件,只要它们不包含空格或其他 shell 解析标记即可。

解决方案 2:

这是一个非常棘手的解决方案,但似乎大多数时候都能正常工作。在测试期间,我注意到在^C命令行上获取时它有时不能很好地工作,尽管我确实对其进行了一些调整,使其表现得更好一些。

此 hack 仅是一种交互模式 hack,我非常有信心我不会向任何人推荐它。后台命令可能会导致比正常情况更不明确的行为。其他答案是通过编程获得结果的更好方法。


话虽如此,这里是“解决方案”:

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

设置此 bash 环境变量并根据需要发出命令。 $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

解决方案 3:

Bash 是一种丑陋的语言。是的,你可以将输出分配给变量

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

但最好希望你尽最大努力find只返回一个结果,并且该结果中不包含任何“奇怪”的字符,例如回车符或换行符,因为它们在分配给 Bash 变量时会被默默修改。

但是在使用变量时最好小心正确引用它!

最好直接对文件采取行动,例如使用find-execdir查阅手册)。

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

或者

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

解决方案 4:

免责声明:

  • 这个答案晚了半年:D

  • 我是 tmux 的重度用户

  • 你必须在 tmux 中运行你的 shell 才能实现此功能

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

  • tmux 捕获窗格:它将显示的数据复制到 tmux 的内部缓冲区之一。它可以复制当前不可见的历史记录,但我们现在对此不感兴趣

  • tmux list-buffers:显示有关已捕获缓冲区的信息。最新的缓冲区编号为 0。

  • tmux show-buffer -b (buffer num):在终端上打印给定缓冲区的内容

  • tmux paste-buffer -b (buffer num):将给定缓冲区的内容粘贴为输入

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

这与程序使用的输出流(stdin 或 stderr)、打印方法(ncurses 等)和程序的退出代码无关 - 只需显示数据。

解决方案 5:

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

v=$(date)
echo $v

您也可以使用反引号。

v=`date`
echo $v

来自Bash 初学者指南,

当使用旧式反引号替换形式时,反斜杠保留其字面含义,除非其后跟有“$”、“`”或“”。第一个前面没有反斜杠的反引号终止命令替换。当使用“$(COMMAND)”形式时,括号之间的所有字符都构成命令;没有一个字符会被特殊处理。

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

解决方案 6:

这很简单。使用反引号:

var=`find . -name foo.txt`

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

echo $var
mv $var /somewhere

解决方案 7:

我认为您也许能够找到一个解决方案,即将您的 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

解决方案 8:

您可以在 bash 配置文件中设置以下别名:

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

然后,通过在任意命令后输入“s”,您可以将结果保存到 shell 变量“it”中。

因此示例用法如下:

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

解决方案 9:

使用反引号捕获输出:

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

解决方案 10:

我只是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

更新:一位匿名用户建议echo用来代替`printf '%s
',其优点是它不会像-e抓取的文本那样处理选项。因此,如果您预计或遇到此类特殊情况,请考虑此建议。另一个选择是使用cat <<<$grab`代替。

解决方案 11:

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

如果是这种情况 - 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/{}

此命令可处理多个文件,即使路径和/或文件名包含空格也能正常工作。

注意命令中的mv {} /some/new/location/{}部分。此命令针对先前命令打印的每一行进行构建和执行。此处,先前命令打印的行被替换为{}

摘自 xargs 的手册页:

xargs-从标准输入构建并执行命令行

有关详细信息,请参阅手册页:
man xargs

解决方案 12:

我通常会按照这里其他人的建议去做......不需要分配:

$find . -iname &#039;*.cpp&#039; -print
./foo.cpp
./bar.cpp
$vi `!!`
2 files to edit

如果你愿意的话,你可以变得更花哨:

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

解决方案 13:

如果您想要的只是重新运行上一个命令并获取输出,那么一个简单的 bash 变量就可以起作用:

LAST=`!!`

因此,您可以使用以下命令在输出上运行命令:

yourCommand $LAST

这将生成一个新进程并重新运行命令,然后为您提供输出。听起来您真正想要的是一个用于命令输出的 bash 历史文件。这意味着您需要捕获 bash 发送到您的终端的输出。您可以编写一些内容来监视必要的 /dev 或 /proc,但这很麻烦。您也可以在您的 term 和 bash 之间创建一个“特殊管道”,并在中间使用 tee 命令重定向到您的输出文件。

但这两种都是黑客的解决方案。我认为最好的办法是使用Terminator,这是一个更现代的终端,具有输出日志记录功能。只需检查日志文件以获取最后一个命令的结果。类似于上面的 bash 变量会使这变得更简单。

解决方案 14:

执行命令并决定将结果存储在变量中后,可以采用以下方法:

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

或者,如果您提前知道想要将结果保存在变量中,则可以使用反引号:

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

解决方案 15:

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

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

正如我所写的,只有当您必须在文件名中预期空格时,差异才是重要的。

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

解决方案 16:

您可以使用!!: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

解决方案 17:

我也有类似的需求,我想将上一个命令的输出用于下一个命令。很像 |(管道)。例如

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

类似于 -

$ which gradle |: ls -altr {}

解决方案:创建此自定义管道。非常简单,使用 xargs -

$ alias :=&#039;xargs -I{}&#039;

基本上没有什么需要为 xargs 创建简写,它就像 charm 一样工作,而且非常方便。我只是在 .bash_profile 文件中添加了别名。

解决方案 18:

可以使用文件描述符和lastpipeshell 选项的魔力来完成它。

它必须通过脚本来完成 - “lastpipe”选项在交互模式下不起作用。

以下是我测试过的脚本:

$ cat shorttest.sh 
#!/bin/bash
shopt -s lastpipe

exit_tests() {
    EXITMSG=&quot;$(cat /proc/self/fd/0)&quot;
}

ls /bloop 2>&amp;1 | exit_tests

echo &quot;My output is &quot;$EXITMSG&quot;&quot;


$ bash shorttest.sh 
My output is &quot;ls: cannot access &#039;/bloop&#039;: No such file or directory&quot;

我在这里做的事情是:

  1. 设置 shell 选项shopt -s lastpipe。如果没有这个选项,它将无法工作,因为您将丢失文件描述符。

  2. 确保我的 stderr 也能被捕获2>&amp;1

  3. 将输出通过管道传输到函数中,以便可以引用 stdin 文件描述符。

  4. /proc/self/fd/0通过获取文件描述符(即 stdin)的内容来设置变量

我用它来捕获脚本中的错误,因此如果命令有问题,我可以停止处理脚本并立即退出。

shopt -s lastpipe

exit_tests() {
    MYSTUFF=&quot;$(cat /proc/self/fd/0)&quot;
    BADLINE=$BASH_LINENO
}

error_msg () {
    echo -e &quot;$0: line $BADLINE
     $MYSTUFF&quot;
    exit 1
}

ls /bloop 2>&amp;1 | exit_tests ; [[ &quot;${PIPESTATUS[0]}&quot; == &quot;0&quot; ]] || error_msg

通过这种方式,我可以2>&amp;1 | exit_tests ; [[ &quot;${PIPESTATUS[0]}&quot; == &quot;0&quot; ]] || error_msg在每一个我想要检查的命令后面添加。

现在您可以享受您的输出了!

解决方案 19:

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

首先让我们看看文件夹“a”里有什么

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

然后,使用 ls 和 cd 的示例将变成 sed 和管道,如下所示:

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

因此,真正的魔法发生在 sed 中,您将任何命令的任何输出都输入到 sed 中,然后 sed 会打印最后一行,您可以将其用作带反引号的参数。或者您也可以将其与 xargs 结合使用。(shell 中的“man xargs”是您的好朋友)

解决方案 20:

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

学习使用 awk 中的管道符号。

find . | awk &#039;{ print &quot;FILE:&quot; $0 }&#039;

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

find . -name &quot;foo.txt&quot; | awk &#039;{ print &quot;mv &quot;$0&quot; ~/bar/&quot; | &quot;sh&quot; }&#039;

解决方案 21:

find . -name foo.txt 1> tmpfile &amp;&amp; mv `cat tmpfile` /path/to/some/dir/

这是另一种方法,尽管比较肮脏。

解决方案 22:

我发现记住将命令的输出传输到特定文件中有点烦人,我的解决方案是我的一个函数,.bash_profile它可以捕获文件中的输出并在需要时返回结果。

这个的优点是你不必重新运行整个命令(当使用find或其他可能很关键的长时间运行的命令时)

很简单,将其粘贴到您的.bash_profile

脚本

# catch stdin, pipe it to stdout and save to a file
catch () { cat - | tee /tmp/catch.out}
# print whatever output was saved to a file
res () { cat /tmp/catch.out }

用法

$ find . -name &#039;filename&#039; | catch
/path/to/filename

$ res
/path/to/filename

此时,我倾向于将其添加| catch到所有命令的末尾,因为这样做没有任何成本,并且可以节省我重新运行需要很长时间才能完成的命令的时间。

此外,如果您想在文本编辑器中打开文件输出,您可以执行以下操作:

# vim or whatever your favorite text editor is
$ vim &lt;(res)
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用