9

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.

Joe
  • 351
  • 2
  • 16
  • Is there any reason why you are not using a reverse search for this (Ctrl and r) ? – Raman Sailopal Jul 06 '17 at 08:20
  • 1
    I never got used to using that. I just tried it. fzf is a lot nicer. It lets me see many alternatives at once with lots more searching options, not just one at a time. And, I didn't see a way to edit what I found. When I pressed Enter, it just ran the unmodified command. Also, if I can figure this out, it might open other possibilities for automatically getting things on the command line for me to work with. – Joe Jul 06 '17 at 08:44
  • 1
    You can press Ctrl-r multiple times to go through the different options related to the text entered. Then you can press escape to get the command on your command line to edit. I would suggest spending time studying the options already available before spending time attempting to design your own – Raman Sailopal Jul 06 '17 at 08:48
  • Also, it you want all the different options relating to the search displayed on screen, you could simply use history and pipe it through grep to search. You can then use ! to action the command – Raman Sailopal Jul 06 '17 at 08:50
  • @RamanSailopal Those are good suggestions. I haven't done much with history other than using the up-arrow to go back. Occasionally, I try an ! followed by some characters, but that's dangerous because it runs it before you see if it selected what you thought it would. Still, fzf appears to be pretty popular, so I am not the only one who finds value in it. Also, these are tools that require a couple of lines of code to implement. I'm not writing my own text editor or a new version of mencoder. – Joe Jul 06 '17 at 09:09

6 Answers6

2

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.

mcint
  • 804
  • 8
  • 18
0

Seems to me that junegunn already solved this problem for you. In this file he replaces the bash ctrl-r with exactly the functionality you are after. I did have a small problem sourcing this file because of the line fzf-file-widget outputing sh: ``fzf-file-widget': not a valid identifier, but after changing that for fzf_file_widget it works great.

sudavid4
  • 1,081
  • 6
  • 14
  • Can you please show an example of how should we use it? I'm using it like this `alias scripts=ls ~/dotfiles/scripts | sed -e 's/\..*$//' | fzf-file-widget` but I'm getting an error `"fzf-file-widget:zle:4: widgets can only be called when ZLE is active"` – Guillermo kuster Sep 12 '19 at 21:01
0

In the current fzf version, ctrl-r behaves exactly like what you want. From the readme:

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

In fact, now some people want the other way around.

M Imam Pratama
  • 998
  • 11
  • 26
0

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.

Joe
  • 351
  • 2
  • 16
0

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.

0

I'm late to the party, but using https://unix.stackexchange.com/questions/391679/how-to-automatically-insert-a-string-after-the-prompt gives the answer for zsh

print -z $(history |sed -re 's/ *[0-9]* *//'|fzf --tac --no-sort)

Did not try with bash.