How to copy an entire command line to my clipboard, without the mouse?

My answer offers a simple command in three different syntaxes, which all work equally, but one is a bit shorter and easier to type than the second. The third variation is a so-called alias, that means you can assign the command a custom name and call it by executing that without having to remember the complex stuff.

Preparation:

First, install the package xsel which allows you to access the X clipboard from the terminal:

sudo apt-get install xsel

Command variation 1 (short):

After that, you can type the following line to copy the previous command to the clipboard:

xsel -ib <<<!!:q

If you want to copy the second latest command, replace !! with !-2, for the third latest use !-3 and so on.

To explain what you're running, here a short break down of the command:

  • xsel is a command-line tool to access the X clipboards.
    For more info, read it's manpage online Manpage icon or by running man xsel.

    • The -i parameter tells xsel to read from the stdin (usually this means keyboard input, but we're going to redirect something here)
    • The -b parameter specifies to use the clipboard instead of X's "primary" or "secondary" selections.
  • <<< is a special Bash syntax called "Here String".
    It basically expands (not evaluates!) the argument (only one!) after it and redirects it as string to the stdin (standard input) of the command before/after which it stands.

  • !!:q is called a "bang command" for history expansion in bash. It replaces itself with any previously typed command line.
    For more info, read it's local manpage by running man history (online manpage is not helpful).

    • The !! stands for the previous command line and is a synonym for !-1.
      Obviously !-2 means then the second last command line. Don't forget the minus sign -, otherwise it will return the 2nd (3rd/...) command you have ever typed.
    • The :q modifies the bang command and tells bash to enclose the substitution in single quotes (') to prevent further expansion by the shell.

Command variation 2 (slightly longer):

echo !!:q | xsel -ib
  • echo has the simple job of printing all its arguments to the terminal's stdout.

  • !!:q is called a "bang command" for history expansion in bash. It replaces itself with any previously typed command line.

    • The !! stands for the previous command line and is a synonym for !-1. Obviously !-2 means then the second last command line. Don't forget the minus sign -, otherwise it will return the 2nd (3rd/...) command you have ever typed.
    • The :q modifies the bang command and tells bash to enclose the substitution in single quotes (') to prevent further expansion by the shell.
  • | is a pipe. It redirects the terminal output ("stdout") of the command before it to the terminal input ("stdin") of the command after it.

  • xsel is a command-line tool to access the X clipboards.
    For more info, read it's manpage online Manpage icon or by running man xsel.

    • The -i parameter tells xsel to read from the stdin (usually this means keyboard input, but we're going to redirect something here)
    • The -b parameter specifies to use the clipboard instead of X's "primary" or "secondary" selections.

Command variation 3 (alias):

A bash alias is a cool thing if you don't want to remember long or complicated commands you often use. You can assign this command to a simple alias name, which you can run instead of the long command to achieve the same.

Unfortunately, as bang commands are a special Bash feature and get expanded before aliases are resolved, you can't simply alias one of the variations above because the !! part will not work. There's a workaround though.

To set the alias, run the following line in your terminal. Note that you may chose any valid Bash variable name instead of copylastcommand:

alias copylastcommand='history -p \!\! | xsel -ib'

This is however only persistent for your current Bash session, which means the alias will vanish after you close the terminal window. You can make it persistent in every of your Bash sessions by adding this line above to the end of your ~/.bashrc file, or to your ~/.bash_aliases file if you have one.

Again, a short break down of the line:

  • alias name='command' is the syntax to set an alias in Bash. The command will be run whenever you execute name from now on.

  • history -p \!\! outputs the previously executed command line to stdout (standard output). Without the -p switch, it would not only print but also run the command again.
    Note that we need to escape the bangs (!) with backslashes (\), because otherwise bash would expand them when we try to set the alias, which makes no sense as they need to be in the alias as they are.
    Again, you can also specify the [n]-th recent command by replacing the second bang with -n, e.g. \!-2.

  • | is a pipe. It redirects the standard output ("stdout") of the command before it to the terminal standard ("stdin") of the command after it.

  • xsel is a command-line tool to access the X clipboards.
    For more info, read it's manpage online Manpage icon or by running man xsel.

    • The -i parameter tells xsel to read from the stdin (usually this means keyboard input, but we're going to redirect something here)
    • The -b parameter specifies to use the clipboard instead of X's "primary" or "secondary" selections.

You can use xclip:

some_command | xclip -selection c

So in you case:

youtube-dl .... | xclip -selection c

c stands for clipboard.

You might need to install xclip first:

sudo apt-get install xclip

EDIT:

If you just want the command you have typed (not the output) to be copied to the clipboard, you can use Here strings:

 xclip -selection c <<<"$(echo foobar)"

Or a pipe:

echo foobar | xclip -selection c

I just made a little script for zsh that uses xclip to do this:

#!/bin/zsh
export HISTFILE=~/.zsh_history
fc -R
fc -l | tail -n 2 | sed -n '1p' | sed 's/[0-9]*  //' | xclip -selection c

This script copies the last command that was entered in the shell/terminal to the system's clipboard. It wouldn't work on a typical remote server.

I thought it would take me just a couple minutes to write this script, but it took surprisingly long to get it working as I stumbled over the way zsh handles history.

The tail -n 2 and first sed in this are dealing with the fact that the command for this script itself is being recorded in the history and so it gets two lines of history and removes the last one.

This script works, and it was sort of a fun puzzle, but I'm really curious if there's a simpler or more elegant way of doing this with zsh.