Copying previous commands with fzf and zsh

Jul 14, 2022

Sometimes I want to copy a command I previously typed in my shell to the clipboard. It may be for documentation, note-taking, writing a script, setting up an Ansible playbook, sending to someone… You name it.

While I could always search the terminal window for it and select it, I have set up an alias that makes things much easier and less error-prone:

alias hy="
  fc -ln 0 |
  awk '!a[\$0]++' |
  fzf --tac --multi --header 'Copy history' |
  xsel --clipboard
"

This will perform the task silently. If you prefer a confirmation message after each copy, a clean approach is to convert the alias into a function like this:

hy() {
  local -r entry="$(fc -ln 0 | awk '!a[$0]++' | fzf --tac --multi --header 'Copy history')"
  [[ -n "$entry" ]] && xsel --clipboard <<< "$entry" >/dev/null && echo "Copied to clipboard." >&2
}

Until now, we assumed the system was running on X11. Now, let’s make it work on Wayland too:

hy() {
  local copy_cmd
  case "$XDG_SESSION_TYPE" in
    x11)     copy_cmd="xsel --clipboard" ;;
    wayland) copy_cmd="wl-copy" ;;
    *) echo "Session type not detected."; return ;;
  esac

  local -r entry="$(fc -ln 0 | awk '!a[$0]++' | fzf --tac --multi --header 'Copy history')"
  [[ -n "$entry" ]] && eval $copy_cmd <<< "$entry" >/dev/null && echo "Copied to clipboard." >&2
}

In action

The alias in action.

Typing hy (short for “history yank”) brings fzf with a list of your most recent commands. You can browse them using the keyboard or perform a fuzzy search. Pressing Enter copies the selected command to the clipboard.

Even better, if you enable multi-selection on fzf, you can select multiple commands using Tab. They will be copied to the clipboard one per line.

The how

Explaining each part:

  • fc -ln 0: On zsh, this returns the entire shell history, one command per line. If you use Bash or another shell, just replace it with the equivalent command.
  • awk '!a[\$0]++': The history is piped to awk, which removes duplicate entries. Without this cleaning, fzf may display multiple repeated lines, which can be annoying.
  • fzf --tac --header 'Copy history': The result is piped to fzf. The --tac argument reverses the order (as we want the last commands to be at the top), while --multi allows multiple selection. We also add a prompt message using --header.
  • wl-copy (on Wayland) or xsel --clipboard (on X11): Finally, the commands selected are copied to the clipboard.

Needless to say, be sure to have the mentioned tools installed on your system for this to work.


Have comments? Email me.

This post was updated:

  • Feb 25, 2024: Add implementation to print a confirmation message.
  • Feb 25, 2024: Replace xclip with xsel.
  • Jul 26, 2024: Add Wayland support.

All posts