如何在bash中执行输出的最后一行?


12

我知道可以使用bash运行bash的最后一条命令!!,但是如何运行输出的最后一行呢?

我正在考虑此输出的用例:

The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

但是我不知道该怎么办。我在想类似!!@@或类似的东西?


超级用户也有这个问题。


1
为什么不复制并粘贴呢?
飞行员

1
@Tim您想要运行程序输出的最后一行的方法,但是对于这种特殊的用例,还有一种更改command_not_found_handleshell函数或它运行的脚本的方法(通常只是/usr/lib/command-not-found)。我认为您可以做到,因此系统会提示您自动安装软件包,或者(可能更好),以便将软件包的名称存储在某个位置,并可以通过运行简短命令进行安装。这与您在此处提出的要求完全不同,您可以考虑发布一个单独的问题。
伊莱亚·卡根


2
可能也相关:github.com/nvbn/thefuck(忽略名称),此工具自动纠正git / apt / etc命令:)
bhathiya-perera

Answers:


16

该命令$(!! |& tail -1)应执行以下操作:

$ git something
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

$ $(!! |& tail -1)
$(git something |& tail -1)
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
git-man liberror-perl

如您所见,sudo apt-get install git命令正在运行。

编辑:分解$(!! |& tail -1)

  • $()bash命令替换模式

  • bash将扩展!!到最后执行的命令

  • |&部分很棘手。通常,管道|将采用左侧命令的STDOUT并将其作为STDIN传递给右侧的命令|,在您的情况下,前一条命令会将其输出打印在STDERR上作为错误消息。因此,这样做|无济于事,我们需要将STDOUT和STDERR(或仅STDERR)传递给右侧命令。|&会将STDOUT和STDERR都作为STDIN传递给右侧命令。或者,更好的是,您只能通过STDERR:

    $(!! 2>&1 >/dev/null | tail -1)
  • tail -1照常会从输入中打印出最后一行。在这里,与打印最后一行相比,您可以更加精确,就像打印包含apt-get install命令的行一样:

    $(!! 2>&1 >/dev/null | grep 'apt-get install')

1
您的方法假设第二次输出将相同。某些命令下一次可能会产生不同的输出,在这种情况下,很难预测执行输出的最后一行实际上要执行的操作。
卡巴斯德(Kasperd),2015年

1
@kasperd您是right..althouh至于这个具体的例子而言,输出应保持不变..
heemayl

7

TL; DR: alias @@='$($(fc -ln -1) |& tail -1)'

Bash的历史记录交互功能不提供任何机制来检查命令的输出外壳程序不存储该内容,历史记录扩展专门用于您自己运行的命令或部分命令。

这样就剩下了重新运行最后一个命令并将stdoutstderr|&)用管道传递到命令替换中的方法。heemayl的答案可以达到此目的,但是不能在别名中使用,因为外壳程序会在扩展别名之前而不是之后执行历史扩展。

我什至无法通过扩展功能在shell函数中使用历史记录扩展,即使通过在功能中启用它也是如此set -H。我怀疑!!会不会被扩展的功能,我不知道它会扩大到如果它是,但现在我不知道正因如此,事实并非如此。

因此,如果您想进行设置以便只需很少的输入就可以执行此操作,则应使用fc内置Shell而不是历史扩展来从历史中提取最后一条命令。这具有额外的优点,即使禁用历史记录扩展,它也可以工作。

如图戈登戴维森答案创建包含bash的历史扩展的别名(上超级用户),$(fc -ln -1)会模拟!!。堵漏这对于!!heemayl的命令 $(!! |& tail -1)得到:

$($(fc -ln -1) |& tail -1)

这类似于,$(!! |& tail -1)但可以使用别名定义:

alias @@='$($(fc -ln -1) |& tail -1)'

在运行该定义或将其放入.bash_aliases.bashrc启动新的外壳程序之后,您可以简单地键入@@(或使用任何别名)来尝试执行上一条命令的最后一行输出。

ek@Io:~$ alias @@='$($(fc -ln -1) |& tail -1)'
ek@Io:~$ evolution
The program 'evolution' is currently not installed. You can install it by typing:
sudo apt-get install evolution
ek@Io:~$ @@
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  evolution-common evolution-data-server evolution-data-server-online-accounts
....

是否可以将其移动到单独的脚本?我尝试了一下,但最终失败了,因为fc没有输出任何内容... fc遵循当前shell会话的历史记录,因此将其移动到单独的脚本中会打开一个具有空历史记录的不同会话,当然...我添加了一些脚本中fc行上方的命令,其中之一已执行。因此,我想知道是否有任何选项可以告诉脚本,其“上下文”是执行该脚本的bash会话。例如,#!/ bin / bash的某些参数或其他内容……
Exterminator13

@ Exterminator13如果您是说要运行一个单独的脚本(例如)./script,而不是您使用.source内置的脚本,则我不确定。Bash会在shell出口处或history使用-a或运行时附加到文件-w(请参阅参考资料help history)。如果确实如此,则将交错多个交互式外壳程序中的命令(按照您运行它们的顺序显示)。您可以立即添加它。。但是我认为要做更多fc工作来编写脚本。我建议对此发布一个新问题。如果您这样做,请随时在此处发表评论并附带其链接。
伊莱亚·卡根

2

如果你正在使用X下的终端仿真器,像gnome-terminalkonsole或者xterm,你可以使用X选择了像复制和粘贴:

使用主要选择

选择整行左键单击三次

然后使用中键单击将其粘贴到命令行

这应该适用于大多数终端仿真器。它利用主要选择,而无需触摸剪贴板-它们是分开的。
从技术上讲,它是一个选择,然后将复制和粘贴合并到一个操作中。


1
@Tim是的-我会说清楚。
Volker Siegel

1

正如Eliah Kagan 提到的那样,shell不存储命令输出。但是,如果您运行screen,则有一种方法可以获取最后一条命令的最后一行输出,而无需再次运行该命令

将以下几行内容输入~/.screenrc

register t "^a[k0y$ ^a]^a^L"
bind t process t

然后,您可以按Ctrl+ at在当前提示中插入上一行。也可以使它也自动按Enter键,但是最好在运行之前检查它,然后手动按Enter键是一个更好的主意。

怎么运行的:

  • register t将字符串注册到名为的缓冲区中t。其后的字符串是键序列。
  • bind t绑定到键t(即使用Ctrl+ 执行at),process t意味着评估名为的缓冲区的内容t
  • 该序列本身意味着:
    • ^a[(= Ctrla[):进入复印模式
    • k向上一行,0转到该行的开头,y$复制到该行的末尾,(空格)完成命令
    • ^a](= Ctrla]):粘贴复制的内容
    • ^a^L(= CtrlaCtrlL)刷新屏幕(否则粘贴的内容将不会显示,因为您自己没有键入内容
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.