Alternative Methods to Ctrl-R Reverse Search in Bash

bashcommand historyreadline

I am happy and really like the CtrlR backward search feature of the bash shell. Some of my colleagues don't like it, since it is sometimes confusing. I understand them. If you enter the wrong characters, the current position in the history is somewhere in the past, and you won't find the recent matches.

Is there a more user friendly alternative for seaching backward in the shell history?

I want to stick with bash. Suggesting an alternative shell is not an answer to this question.

The issue with the "lost position" is explained here: Reset bash history search position. These solutions work. That's right. But the solution there are not easy and user friendly according to my point of view. These solutions are not simple and straight forward. These are solutions of the past. In the past the human needed to learn the way the computer wanted the input. But today the tools should accept the input in a way which is easy for the user.

Maybe someone knows a IDE of jetbrains like PyCharm. If you search for "foobar" you even get the lines which contain "foo_bar". That's great, that's unix 🙂

Best Answer

I'm using the fuzzy finder program FZF. I've written my own key bindings and shell scripts to utilize FZF as my tool of choice to reverse-search an interactive Bash shell's history. Feel free to copy and paste the code from my Config GitHub repository.

~/.bashrc configuration file

# Test if fuzzy finder program _Fzf_ is installed.
#
if type -p fzf &> /dev/null; then

  # Test if _Fzf_ specific _Readline_ file is readable.
  #
  if [[ -f ~/.inputrc.fzf && -r ~/.inputrc.fzf ]]; then

    # Make _Fzf_ available through _Readline_ key bindings.
    #
    bind -f ~/.inputrc.fzf
  fi
fi

~/.inputrc.fzf configuration file ##

$if mode=vi

  # Key bindings for _Vi_ _Insert_ mode
  # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  set keymap vi-insert

  "\C-x\C-a": vi-movement-mode
  "\C-x\C-e": shell-expand-line
  "\C-x\C-r": redraw-current-line
  "\C-x^": history-expand-line
  "\C-r": "\C-x\C-addi$(HISTTIMEFORMAT= history | fzf-history)\C-x\C-e\C-x\C-r\C-x^\C-x\C-a$a"

  # Key bindings for _Vi_ _Command_ mode
  # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  set keymap vi-command

  "\C-r": "i\C-r"
  "\ec": "i\ec"

$endif

fzf-history executable Bash script

#!/usr/bin/env bash
#
# Retrieve command from history with fuzzy finder
# ===============================================
# Tim Friske <me@tifr.de>
#
# See also:
#   * man:bash[1]
#   * man:fzf[1]
#   * man:cat[1]

shopt -os nounset pipefail errexit errtrace
shopt -s extglob globstar

function print_help {
  1>&2 cat \
<<'HELP'
usage:
  HISTTIMEFORMAT= history | fzf-history
HELP
}

function fzf_history {
  if [[ -t 0 ]]; then
    print_help
    exit
  fi

  local fzf_options=()
  fzf_options+=(${FZF_DEFAULT_OPTS:-})
  fzf_options+=('--tac' '-n2..,..' '--tiebreak=index')
  fzf_options+=(${FZF_HISTORY_FZF_OPTS:-})
  fzf_options+=('--print0')

  local cmd='' cmds=()
  while read -r -d '' cmd; do
    cmds+=("${cmd/#+([[:digit:]])+([[:space:]])/}")
  done < <(fzf "${fzf_options[@]}")
  if [[ "${#cmds[*]}" -gt 0 ]]; then
    (IFS=';'; printf '%s\n' "${cmds[*]}")
  fi
}

fzf_history "$@"

key-bindings.bash sourceable Bash script

Taken and slightly adapted from FZF's Bash key bindings file here are the Emacs mode compatible key bindings for Bash's history reverse-search with Ctrl-R (untested):

if [[ ! -o vi ]]; then
  # Required to refresh the prompt after fzf
  bind '"\er": redraw-current-line'
  bind '"\e^": history-expand-line'

  # CTRL-R - Paste the selected command from history into the command line
  bind '"\C-r": " \C-e\C-u\C-y\ey\C-u$(HISTTIMEFORMAT= history | fzf-history)\e\C-e\er\e^"'
fi