15

Linux (e.g. Ubuntu) terminal, is there a method to get the last line? Say I am randomly typing a td command, which has not been installed on my system, so I will get a message like below. I would like to have a "shortcut" to run sudo apt-get install textdraw quickly.

Is there such a tool or how can I copy the last line to clipboard without using the mouse?

username@WorkStation:~$ td
The program 'td' is currently not installed. You can install it by typing:
sudo apt-get install textdraw
username@WorkStation:~$
3
  • Press the up arrow? Have you tried that? Also check out the history command in Linux, that might work for you as well.
    – Richie086
    Dec 28, 2015 at 7:18
  • Terminal multiplexer programs, tmux and screen, provide the capability of getitng text into a clipboard. However, there might be an easier way in Ubuntu's default GUI, so I suspect an easier answer may be available. Pressing PgUp, Shift-PgUp, or Ctrl-PgUp might allow for scrollback.
    – TOOGAM
    Dec 28, 2015 at 7:45
  • 3
    @Richie086 Please note, what I need is sudo apt-get install textdraw, not td. up arrow will only give me the td
    – Daniel
    Dec 28, 2015 at 8:47

5 Answers 5

7

If you don't mind a little obscenity (I don't), you might want to use the fuck, a tool that does exactly what you asked for.

Well, not exactly, but it solves the same problem. Instead of just getting the latest line, it tries to match the last command you typed.

The Fuck tries to match a rule for the previous command, creates a new command using the matched rule and runs it.

The examples shown in the repository show several of the scenarios you mentioned.

enter image description here

0
3

In tmux v2.4 and onwards (since this commit https://github.com/tmux/tmux/commit/76d6d3641f271be1756e41494960d96714e7ee58) with send-keys -X. It might be possible in older versions, with a different syntax.

In .tmux.conf:

bind ! copy-mode \;\
       send-keys -X cursor-up \;\
       send-keys -X select-line \;\
       send-keys -X cursor-left \;\
       send-keys -X copy-selection-and-cancel \;\
       paste-buffer

Now, prefix+! will copy the last line at the current cursor position.

cursor-left can be left out if you want to execute it directly without typing return.

Note : it won't work if the last line is empty or if it wrapped but it's still useful in most cases

1
  • Thanks. But, to prevent execution i have to use paste-buffer -s ''.
    – mykhal
    Apr 20, 2023 at 9:15
2

The short answer: no, probably you have no available method for doing this.

The long answer:

  • while it is possible to have a terminal emulator which allows you to "just" read back the screen contents and edit them, in practice that isn't done often due to concerns that some unwanted program could snoop on your keyboard entries.
  • a shell could be designed capture the output of your commands and allow you to edit that (just like your command history). It would get complicated if the command was something like vi (where the output isn't nice line-oriented text). bash doesn't do that, anyway.

In practice, what people do to work around this limitation is use other programs to capture the command-output, and edit that. But it won't solve your immediate problem.

For example, I use vi-like-emacs to issue ad hoc shell commands. If those do not require further input, it works well to run those in a window, capturing the output of the command — and editing the output as needed to refine the command. Emacs also allows you to do something like that, as well as vim.

2

If you got that message in ubuntu after running a non-existing command from bash, your system is probably using command_not_found_handle function. You can see it in /etc/bash.bashrc.

A trivial hacking may be an option: I just created a script named cnfh:

#!/bin/bash

# if the command-not-found package is installed, use it
if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then
        function command_not_found_handle {
                # check because c-n-f could've been removed in the meantime
                if [ -x /usr/lib/command-not-found ]; then
                   /usr/lib/command-not-found -- "$1"
                   return $?
                elif [ -x /usr/share/command-not-found/command-not-found ]; then
                   /usr/share/command-not-found/command-not-found -- "$1"
                   return $?
                else
                   printf "%s: command not found\n" "$1" >&2
                   return 127
                fi
        }
fi


"$@"
RET_VAL=$?
if [ $RET_VAL -eq 127 ]; then
  OUT=$(command_not_found_handle "$@" 2>&1)
  $(echo $OUT |sed -n 's/.*\(apt-get install .\+\)$/\1/p')
fi

Then run td command using this script:

# ./cnfh td

Using Ubuntu 14.04.2 LTS. I hope that helps.

1
  • This looks really cool and maybe something I'd like to add to my system. Could you add some information on how it works? The built-in code for handling completions and things like this is pretty hard to understand.
    – Joe
    Dec 28, 2015 at 19:51
1

To execute the last line of output from the most recent command, if you’re willing to re-execute that command (since it didn’t do anything except issue a diagnostic message), do

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

Step by step (layer by layer):

  • fc (which might stand for “Fix Command”) is a shell builtin command for accessing command history.

    • -ln
      • l (lower case L) — list previous command(s)
      • n — do not include command numbers
    • 1 (one) — refers to the most recent command

    fc -ln -1 lists the most recent command, not including a command number.

  • $(fc -ln -1) executes the most recent command.
  • $(fc -ln -1) | tail -n 1 executes the most recent command and displays the last line of its output.  This is the sudo apt-get install textdraw line, in your example.
  • $($(fc -ln -1) | tail -n 1) executes that command.

You can alias this:

alias execute_last_line_of_output_from_previous_command='$($(fc -ln -1) | tail -n 1)'

I leave it to you to choose a shorter name for the alias.

Notes:

  • This works only for simple commands — no special characters like |, <, >, ;, &, or even quotes.  If you need to handle complex commands, you can probably do it by adding an eval, but this is widely not recommended.
  • If you need to access something other than the last line of output (e.g., the seventeenth-to-last last line), you can do something line tail -n 17 | head -n 1.
  • If you need to access something other than the most recent command, you can alter the arguments to fc.  Note that fc -ln -1 is short for fc -ln -1 -1.  To retrieve the third most recent command, use fc -ln -3 -3.
  • If you need flexibility like the above, you should probably write a shell function that takes arguments rather than an alias.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .