I know I can run the last command in bash, with !!
, but how can I run the last line of output?
I am thinking of the use case of this output:
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git
But I don't know how I can run that. I'm thinking of something like the !!
, maybe @@
or similar?
The command
$(!! |& tail -1)
should do:As you can see the
sudo apt-get install git
command is running.EDIT : Breaking down
$(!! |& tail -1)
$()
is thebash
command substitution patternbash
will expand!!
to the last executed command|&
part is tricky. Normally pipe|
will take the STDOUT of the left sided command and pass it as STDIN to the command on the right side of|
, in your case the previous command prints its output on STDERR as an error message. So, doing|
would not help, we need to pass both the STDOUT and STDERR (or only STDERR) to the right sided command.|&
will pass the STDOUT and STDERR both as the STDIN to the right sided command. Alternately, better you can only pass the STDERR:tail -1
as usual will print the last line from its input. Here rather than printing the last line you can be more precise like printing the line that contains theapt-get install
command:TL;DR:
alias @@='$($(fc -ln -1) |& tail -1)'
Bash's history interaction facilities don't offer any mechanism to examine the output of commands. The shell doesn't store that, and history expansion is specifically for commands you have yourself run, or parts of those commands.
This leaves the approach of rerunning the last command and piping both stdout and stderr (
|&
) into a command substitution. heemayl's answer achieves this, but cannot be used in an an alias because the shell performs history expansion before expanding aliases, and not after.I can't get history expansion to work in a shell function either, even by enabling it in the function with
set -H
. I suspect!!
in a function will never be expanded, and I'm not sure what it would be expanded to if it were, but right now I'm not sure precisely why it isn't.Therefore, if you want to set things up so you can do this with very little typing, you should use the
fc
shell builtin instead of history expansion to extract the last command from the history. This has the additional advantage that it works even when history expansion is disabled.As shown in Gordon Davisson's answer to Creating an alias containing bash history expansion (on Super User),
$(fc -ln -1)
simulates!!
. Plugging this in for!!
in heemayl's command$(!! |& tail -1)
yields:This works like
$(!! |& tail -1)
but can go in an alias definition:After you run that definition, or put it in
.bash_aliases
or.bashrc
and start a new shell, you can simply type@@
(or whatever you named the alias) to attempt to execute the last line of output from the last command.If you are using a terminal emulator under X, like
gnome-terminal
,konsole
orxterm
, you can use the X selection for something like copy and paste:Use the primary selection
Select the whole line to run with a triple left click.
Then paste it to the command line using a middle button click.
This should work in most terminal emulators. It makes use of the primary selection, without touching the clipboard - they are separate.
It's selecting, and then combined copy and paste in one operation, technically.
As Eliah Kagan mentioned, the shell does not store the command output. However, there is a way to get the last command's last line of output, without running the command again, if you run screen.
Put the following lines into your
~/.screenrc
:Then you can press Ctrl+at to insert the previous line in the current prompt. It would be possible to make this also automatically press enter, but it's probably a better idea to check it before you run and just press enter manually.
How it works:
register t
registers a string into a buffer namedt
. The string following that is a key sequence.bind t
binds to the key t (i.e. execute using Ctrl+at),process t
means evaluate the content of the buffer namedt
.^a[
(= Ctrla[): enter copy modek
go one line up,0
go to the beginning of the line,y$
copy until end of the line, (space) complete the command^a]
(= Ctrla]): paste copied content^a^L
(= CtrlaCtrlL) refresh the screen (otherwise the pasted content won't show up, because you didn't type it yourselfTL;DR: Should you need to run the
secondlast
line of output of the last command:In Ubuntu 19.10, when a command is not found and the
sudo apt install
command is displayed, there's a blank line before the next command prompt so nothing happens when using:alias @@='$($(fc -ln -1) |& tail -1)'
In this case you have to grab the
secondlast
line of output of the last command:alias @@='$($(fc -ln -1) |& tail -2 | head -1)'