Get output from fzf in the terminal without executing it

Viewed 4656

fzf rocks! I want to use it to search bash history. When a match is found, I want it to be placed on the command line, but not executed so I can edit it further. (All of this is in a sourced bash function or script so it has access to my history.)

I have it almost working, but not quite.

This selects the command, but runs it immediately:

eval $(history | "$HOME/bin/fzf" +s | sed 's/ *[0-9]* *//')

This gets it into my clipboard, but when I paste it, it still ends with a blank and a newline so it executes immediately.

history | "$HOME/bin/fzf" +s | sed 's/ *[0-9]* *//' | xclip -se c

I also tried a couple of variations including

history | "$HOME/bin/fzf" +s | sed -e 's/ *[0-9]* *//' -e 's/$//' | xclip -se c

with the same result.

This actually works, but I still have to put it in the clipboard and then manually paste it.

history | "$HOME/bin/fzf" +s | sed -re 's/ *[0-9]* *//' | awk '{printf "%s", $0}' | xclip -se c

How can I do this in one step - without a manual paste?

I thought just leaving off the xclip would do it, but it doesn't work.

5 Answers

fzf comes ready to install this functionality in fzf's key-bindings.bash mentioned by @sudavid4. The __fzf_history__ function searches bash history interactively using fzf.

In default history handling (though unnecessary with fzf's function), editing after selection is enabled by setting histverify. From man bash:

histverify

If set, and readline is being used, the results of history substitution are not immediately passed to the shell parser. Instead, the resulting line is loaded into the readline editing buffer, allowing further modification.

Finally, bash's builtin fc (learn more with help, not man), supports "Display[ing] or execut[ing] commands from the history list" in addition to the history builtin which focuses on "Display or manipulat[ion of] the history list."


Note also...

fzf's key-bind.bash snippet:

  # CTRL-R - Paste the selected command from history into the command line
  bind -m emacs-standard -x '"\C-r": __fzf_history__'
  bind -m vi-command -x '"\C-r": __fzf_history__'
  bind -m vi-insert -x '"\C-r": __fzf_history__'

A neighboring bash shopt (shell option) with related but different purpose:

histreedit

If set, and readline is being used, a user is given the opportunity to re-edit a failed history substitution.

In the current fzf version, you can use ctrl-r to do it from the command line. From the readme:

CTRL-R - Paste the selected command from history onto the command-line

I didn't realize this never got fully answered. I ended up writing a function which is in $HOME/.bashrc.

It still requires pressing paste (Ctrl+Shift+v) to get the result on the command line.

# fh - repeat history
fh () 
{ 
    history | tac | "$HOME/bin/fzf" +s | sed -re 's/ *[0-9]* *//' | awk '{printf "%s", $0}' | xclip -se c
}

It leaves the selected command line in the clipboard so you can paste it into the command line for further editing and execution.

It pipes the CLI history into tac which reverses the order so the most recent commands show up first (at the end) and sends it on to fzf. The +s tells fzf not to sort the output again. Next, sed is used to strip off the command number prefix from the result. It then goes through awk to strip the trailing newline from the command so it won't run as soon as it is pasted. Finally, xclip is used to put the selection into the clipboard.

For the final touch, I add

bind '"\C-F":" fh\n"'  # fzf history on ctrl+F

to .bashrc to bind the function to Ctrl+f to make it easy to use and remember.

I added a leading space to the bound fh command so that it isn't added to command history when it is invoked.

I wanted this behaviour for opening files (not history) via fzf.

Checking the link shared by @mcint: fzf's key-bind.bash helped me solve my usecase:

CTRL-T: Paste the selected file path into the command line.

E.g. vim CTRL-T

By using this method, command with expanded filename shows up in bash history.

Related