20

Imagine that I have this line in my terminal:

youtube-dl --get-thumbnail --extract-audio --audio-format mp3 https://www.youtube.com/watch?v=

After testing it, I would like to copy this line above to my clipboard and paste it in some script file. Not the output of the command. But, doing this without using the mouse. I like to keep my hands at the keyboard only. I think is faster that way, to do some coding :)

22

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.
  • 1
    Nice answer, but could you avoid the pipe? Pretty please? <<<"!!" xsel -ib – kos Nov 1 '15 at 17:26
  • @kos yes, thanks. That makes it a bit easier. Could you help my mind to remember how the <<< structure is called again? And I think <<<!!:q xsel -ib is better (letting the history bang command do the quoting), or do you disagree? – Byte Commander Nov 2 '15 at 18:21
  • But !!:q uses single quotes (')... – Byte Commander Nov 2 '15 at 19:20
  • You're right, expansion would be performed using <<<"!!" xsel -ib. Yes, <<<!!:q xsel -ib is better, go for it. – kos Nov 2 '15 at 19:58
  • Nice, xsel has interesting syntax – Sergiy Kolodyazhnyy Jan 22 '17 at 21:49
5

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
  • 1
    I still get the commands output, but I just want the line itself. – Valter Silva Nov 1 '15 at 15:44
  • echo "command" | xclip -selection clipboard... Or make it a script under ~/.bin: echo "$@" | xclip -selection clipboard. Say you save it as "cpy", then you could cpy youtube-dl stuff and it would be copied to the clipboard without being executed. – Alex Nov 1 '15 at 15:53
  • And if you want it to be executed... echo "$@" | xclip -selection c && $@, or something more clever with eval... – Alex Nov 1 '15 at 15:55
  • @ValterHenrique Please check my edits.. – heemayl Nov 1 '15 at 15:59
3

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.

  • Well you could probably simplify the tail and sed commands into a one or two commands. Those commands use !! which is explained here - it should be available in ZSH.... – Wilf May 25 '17 at 17:27
  • Oh, !! indeed looks like it would be helpful here, thanks! – alec May 25 '17 at 17:39
0

I am using as a clipboard manager, so.

alias pbpaste='xclip -i -selection clipboard -o'
alias pbcopy='xclip -selection clipboard'
vimscratch='vim -c 'setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile' 

Last command to clipboard

alias last2cb='fc -ln -1 | pbcopy'

Clipboard to vim scratch

alias vcb='pbpaste | vimscratch -'

ZSH specific

# Copy the most recent command to the clipboard
function _pbcopy_last_command(){
    fc -ln -1 | pbcopy
}
zle -N pbcopy-last-command _pbcopy_last_command
bindkey '^x^y' pbcopy-last-command
# Ctrl-x Ctrl-y  to copy last command to the clipboard


# Edit content of clipboard on vim (scratch buffer)
function _edit_clipboard(){
    pbpaste | vim -c 'setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile' -
}
zle -N edit-clipboard _edit_clipboard
bindkey '^x^v' edit-clipboard
# Ctrl-x Ctrl-v to edit clipboard on vim


# define function that retrieves and runs last command
function run-again {
    # get previous history item
    zle up-history
    # confirm command
    zle accept-line
}
# define run-again widget from function of the same name
zle -N run-again
bindkey '\er' run-again
# alt-r to run last command again

I think these ideas can help you to create your own solution

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.