118

In zsh, I know that I can search history with Ctrl+r. However, oftentimes I start to type a command directly at the prompt, but then realize I should be searching history. When I hit Ctrl+r, it brings up a blank history search prompt like this:

history search prompt not pre-filled

Notice how there is text at my prompt but not at the history search prompt. How do I start the history search with the text already in the prompt, so it looks like this:

history search prompt pre-filled

14 Answers 14

114

You can use zle's history-search functionality:

bindkey "^[[A" history-beginning-search-backward
bindkey "^[[B" history-beginning-search-forward

This binds Up and Down (adjust for your own escape sequences) to a history search, backwards and forwards, based upon what has already been entered at the prompt.

So, if you were to enter "vim" and hit Up, zsh will traverse backwards through your history for only those commands commencing with "vim".

You can additionally have the cursor placed at the end of the line once you have selected your desired command from zsh's history by using the history-search-end function (typically located in /usr/share/zsh/functions/Zle/) and appending -end to the end of each line, like so:

autoload -U history-search-end
zle -N history-beginning-search-backward-end history-search-end
zle -N history-beginning-search-forward-end history-search-end
bindkey "^[[A" history-beginning-search-backward-end
bindkey "^[[B" history-beginning-search-forward-end
13
  • Great, thank you. I had to remove the "-end" for both bindkey commands to make them work though. I have not edited your answer because maybe there's some situation in which those are necessary? Oct 27, 2013 at 23:55
  • No problem: updated the answer with the detail you wanted clarified.
    – jasonwryan
    Oct 28, 2013 at 0:07
  • 3
    @hanielabed Oh-my-Zsh is a clusterfsck; I am not at all surprised it would break something as fundamental as history search...
    – jasonwryan
    Dec 18, 2015 at 6:24
  • 1
    Is there a way for the cursor to jump to the end from an empty history search, but stay the same position for a partial search? This is what happens in bash currently. Jan 11, 2017 at 4:35
  • 2
    This didn't work on macOS. I needed to use bindkey "$terminfo[kcuu1]" history-beginning-search-backward-end using zzxyz's suggestion below.
    – pheon
    Mar 13, 2018 at 21:03
36

Another useful option is history | grep

Assign an alias, e.g.

alias hg='history | grep'

then you can type hg whatever to search for commands you've used, e.g.

$ hg chmod                                                                                                       
 1309  chmod +x rotate_files.sh 
 1385  chmod +x rotate_files_270.sh 
 1512  chmod +x testy.sh 
 1528  chmod +x act_on_2_numbers.sh 
 2142  chmod +x ~/bin/display_tmux_pane_pwd.sh
 4532  chmod +x cat_files.rb 

I put this alias in my dot files.

4
  • 32
    What does hg remind me of... Oh yeah, Mercurial!
    – user42117
    Mar 27, 2015 at 19:57
  • 4
    Of what does Hg remind me? Well, mercury, the element with atomic number 80. Also known as quicksilver, which reminds me of en.wikipedia.org/wiki/Quicksilver_(software)
    – David J.
    Jun 11, 2020 at 15:50
  • 1
    @user42117 Shhhh!.. Don't scare all these Github-born millennials.
    – ratijas
    Sep 29, 2020 at 15:34
  • 4
    This alias should probably be updated to use the zsh full history command history 1 (otherwise it will only grep over the most recent 15 history items) - see: stackoverflow.com/questions/26846738/zsh-history-is-too-short
    – Pierz
    Apr 26, 2021 at 13:45
29

If you are using oh-my-zsh, add history-substring-search to the plugins=(...) line.

Then add

bindkey "^[[A" history-substring-search-up
bindkey "^[[B" history-substring-search-down

somewhere below the line that read source $ZSH/oh-my-zsh.sh. Save and fire up a new terminal or run source ~/.zshrc in the current terminal.

Note: ^[[A is the escape sequence for up arrow in the terminal I use (kitty) and many others. To check in your terminal of choice, type in showkey -a and then press they key you want to find the escape sequence for.

6
  • It is unclear what this keymapping does, the answer above explains "This binds Up and Down (adjust for your own escape sequences) to a history search, backwards and forwards, based upon what has already been entered at the prompt."
    – Colin D
    May 31, 2019 at 4:10
  • @ColinD Added a note to clarify Jun 4, 2019 at 22:45
  • 3
    substring search is best, its a superset of command begins with. to implement without being an oh-my-zsh user I did brew install zsh-history-substring-search, checked the repo readme for instructions, I'll just go ahead and create an answer I'm really happy with the result.
    – Merlin
    Sep 8, 2019 at 5:20
  • 3
    +1 for getting to know showkey! It should be a common knowledge. How I even survived without it 10+ years including time spent in vim?
    – ratijas
    Sep 29, 2020 at 15:36
  • It's unclear why the bindkey needs to be placed below source $ZSH/oh-my-zsh.sh.
    – payne
    Aug 31, 2023 at 19:04
13

I'd highly recommend using "$terminfo[kcuu1]" or "$key[Up]" rather than hard-coded stuff like "^[[A" which may or may not work on any particular system.

Check out /etc/zsh/zshrc for more keys. Here's what it looks like on my system. I think the terminfo keys are more likely to be defined.

key=(
    BackSpace  "${terminfo[kbs]}"
    Home       "${terminfo[khome]}"
    End        "${terminfo[kend]}"
    Insert     "${terminfo[kich1]}"
    Delete     "${terminfo[kdch1]}"
    Up         "${terminfo[kcuu1]}"
    Down       "${terminfo[kcud1]}"
    Left       "${terminfo[kcub1]}"
    Right      "${terminfo[kcuf1]}"
    PageUp     "${terminfo[kpp]}"
    PageDown   "${terminfo[knp]}"
)
6
  • Any example where Up isn’t ^[[A? It seems to be the case for most (if not all) of my computers. Apr 10, 2020 at 6:29
  • @FranklinYu - Nothing concrete anymore, I'm afraid, but..between Cygwin, WSL, ssh into remote Ubuntu machines, Oracle machines, x-windows terminals, and various MS Windows terminals (of varying quality), it has definitely been a significant issue for me. The terminfo keys are almost always available and are definitely more reliable when they are.
    – zzxyz
    Apr 11, 2020 at 19:22
  • 2
    Immaterial whether there are or aren't. When there are readable defines or macros to take the guesswork out, use them.
    – RichieHH
    Aug 15, 2020 at 0:22
  • @FranklinYu ^[[A didn't work in my tmux session but $key[Up] worked.
    – Gan Quan
    Aug 6, 2022 at 20:08
  • @GanQuan In tmux my $key[Up] is the same as ^[[A. What escape code does your up-arrow produce? (You can tell with Ctrl-V mode.) Aug 10, 2022 at 5:07
13

Third part solutions

1 - What about using zsh-autosuggestions?

The thing is, for each command you type this plugin will show you some suggestions that could be accepted or not. Try it out and give us some feedback.

2 - The fzf after installed and properly configurated add an widget that changes your reversed search history (by default binded to fzf-history-widget Ctrlr), it adds some fuzzy search on your command history. I am using it and combined with the autosuggestions works like a charm.

Native solution

With no plugins we can press Ctrlr to perform a reverse seach on the history, as soon as you start typing the matched commands will appear as suggestions.

3
  • 1
    The ctrl-r solution should be number one. It's useful and always available.
    – not2savvy
    Jan 4, 2022 at 18:16
  • 1
    This plugin is what I was looking for!
    – daviewales
    Feb 16, 2022 at 22:07
  • It's very useful! Jun 5, 2022 at 19:19
9

You might want to use the script at https://github.com/zsh-users/zsh-history-substring-search

Where you can type in any part of any previously entered command and press the Up and Down keys to cycle through the matching commands.

6

None of the existing answers begin an incremental search with what's been typed so far, like the question asked. This is actually possible:

_history-incremental-search-backward () {
    zle .history-incremental-search-backward -- $BUFFER
}
zle -N history-incremental-search-backward _history-incremental-search-backward

# this line is actually not necessary since this is default.
bindkey '^R' history-incremental-search-backward
  • we make a custom zle widget to wrap history-incremental-search-backward and pass the $BUFFER variable which contains the contents of the zsh prompt typed so far. You can then continue typing in the minibuffer to further narrow your search.

Naming the function after the widget allows us to simplify the code:

history-incremental-search-{back,for}ward() zle .$WIDGET -- $BUFFER
zle -N history-incremental-search-backward
zle -N history-incremental-search-forward

(here also extended to do it also for the forward search)

$BUFFER expands to the full contents of the editing buffer, replace with $LBUFFER if you only want to search based on what's left of the cursor.

Screenshots

  • start typing a command

    enter image description here

  • realize you want to search in your history, press Ctrl-R, a minibuffer is opened and filled with the contents of the command line. As such, it first matches the command line just typed:

    enter image description here

  • press Ctrl-R again, it matches a previous command, Tab or Ctrl-E to accept the result.

    enter image description here

  • or continue typing to refine the search further

    enter image description here

To avoid the first backward search matching on the current buffer, we can empty the buffer before calling the original search widget and restore it upon failure:

history-incremental-search-{back,for}ward() {
  local saved_BUFFER=$BUFFER saved_CURSOR=$CURSOR error
  BUFFER=
  zle .$WIDGET -- $saved_BUFFER
  error=$?
  if (( error )) BUFFER=$saved_BUFFER CURSOR=$saved_CURSOR
  return error
}
zle -N history-incremental-search-backward
zle -N history-incremental-search-forward
2
  • This is God-tier answer. Should be the basis of several SO's/SE's questions' accepted answers. You're the only one I've seen point out the $BUFFER variable, and this is the closest thing to enabling truly vi-like search - both in the line and then across the history!
    – mtraceur
    Jun 17, 2021 at 16:53
  • There is at least one quirk that I'd like to one day spend some time working around, but this is by far the closest to what I've wanted without requiring me to install a plugin.
    – mtraceur
    Jun 17, 2021 at 16:54
3

I use the vi-mode plugin. And then in my ~/.zshrc I have the following bindings:

bindkey "^P" history-beginning-search-backward
bindkey "^N" history-beginning-search-forward

That enables me to use the ^P and ^N as normal backward and forward search if I don't enter any text, and as filtered search if I do.

2

Non-Oh-My-Zsh answer

This worked for me (on macOS) to get substring history easily accessible via up/down arrow. The key binding is up to you.

Substring search history is better than just accessing commands that start with a string. This is more like ctrl-r in most cases, those without globs or regex matching.

Starting from instructions at zsh-users/zsh-history-substring-search

brew install zsh-history-substring-search

# add the following to .zshrc
source /usr/local/share/zsh-history-substring-search/zsh-history-substring-search.zsh
bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down

Now enjoying this thoroughly.

1
  • the new path in mac, /opt/homebrew/share/zsh-history-substring-search/zsh-history-substring-search.zsh Nov 8, 2023 at 13:20
1

I agree with cheflo that "substring" search is more useful. I cannot comment there so I started a new answer. I use oh-my-zsh, and add "history-substring-search" to the plugins=(...) of ~/.zshrc. Then source ~/.zshrc. Do not need to add anything more. Use "bindkey" to verify:

"^[OA" history-substring-search-up
"^[OB" history-substring-search-down

Later, you type "to" and up arrow, "history" will appear and "to" highlighted (if you used "history" command before).

1

You could instead use zaw, which completely replaces your search with a much better multi keyword super search, and fixes your problem as well - searching history is only one of the things zaw can search.

Small intro/discussion here

2
  • How is Zaw compared to history-incremental-pattern-search-forward? Pattern searching seems to cover the use-case. For example if you want to search both foo and bar then you can search for pattern of foo*bar. Apr 10, 2020 at 6:45
  • @FranklinYu - i'd say the other advantage zaw brings to the table is it does a nice full screen search, lets you edit and bookmark history selections, and lets you do the same with other sources, plus you can add your own sources too.
    – Brad Parks
    Apr 10, 2020 at 11:28
1

For those who want to know more about jasonwryan'a answer

This is the mentioned file:

# function history-search-end {
#
# This implements functions like history-beginning-search-{back,for}ward,
# but takes the cursor to the end of the line after moving in the
# history, like history-search-{back,for}ward.  To use them:
#   zle -N history-beginning-search-backward-end history-search-end
#   zle -N history-beginning-search-forward-end history-search-end
#   bindkey '...' history-beginning-search-backward-end
#   bindkey '...' history-beginning-search-forward-end

integer cursor=$CURSOR mark=$MARK

if [[ $LASTWIDGET = history-beginning-search-*-end ]]; then
  # Last widget called set $MARK.
  CURSOR=$MARK
else
  MARK=$CURSOR
fi

if zle .${WIDGET%-end}; then
  # success, go to end of line
  zle .end-of-line
else
  # failure, restore position
  CURSOR=$cursor
  MARK=$mark
  return 1
fi
# }

zle -N widget [ function ] : Create a user-defined widget.
When the new widget is invoked from within the editor, the specified shell function is called.
If no function name is specified, it defaults to the same name as the widget.
It is recommended that user-defined widgets should not have names starting with ..

# To make  history-beginning-search-{back,or}ward-end  an alias    for history-search-end
zle -N history-beginning-search-backward-end history-search-end
zle -N history-beginning-search-forward-end history-search-end
# why `history-beginning-search-{back,or}ward-end` is chosen?
# There is widgets named `history-beginning-search-{back,or}ward`,
# so just add 'end' to them, showing their relevance.

I think, (not quite sure), without this line if [[ $LASTWIDGET = history-beginning-search-*-end ]] in the file above file, the code

zle -N history-beginning-search-backward-end history-search-end
zle -N history-beginning-search-forward-end history-search-end
bindkey "^[[A" history-beginning-search-backward-end
bindkey "^[[B" history-beginning-search-forward-end

can be replaced by:

bindkey "^[[A" history-search-end
bindkey "^[[B" history-search-end
# less word to type

(Just to show the usage of zle and bindkey. Now that we have that if line, don't use the simplified one.)

zsh's bindkey command

bindkey -v: select viins keymap and bind it to main
bindkey -e: select emacs keymap and bind it to main

-A : create alias (aka link)
bindkey -A viins main makes viins an alias for main

Either emacs or viins is linked to the name main.

If one of the VISUAL or EDITOR environment variables contain the string vi when the shell starts up,
then it will be viins,
otherwise it will be emacs.

Create a new keymap from emacs:

  • bindkey -N mymapname emacs

To use "mymap":

  • bindkey -A mymapname main
1
  • 1
    Welcome to the site. Please don't post screenshots of console output or file content. They are often difficult to read, the content will not show up in search engine results, and the OP will need to type-copy command examples when trying to apply your solution. Instead, paste it into the question using code formatting.
    – AdminBee
    Nov 12, 2021 at 7:46
0

Because I use oh-my-zsh, and I use the substring solution from joelostblom, James, I modify the .zshrc like this: (which at line 13, find the line "source $ZSH/oh-my-zsh.sh ", then modify code around it.)

plugins=(
  git
  history-substring-search
)

source $ZSH/oh-my-zsh.sh

bindkey -v
bindkey "^[[A" history-substring-search-up
bindkey "^[[B" history-substring-search-down
1
  • 2
    bindkey -v will break ctrl-a, ctrl-e. Most likely all ctrl commands. Oct 15, 2019 at 2:55
0

My recommended solution for searching history with the text already entered at the prompt in zsh is to use the fzf utility. Fzf is a versatile command-line fuzzy finder that can greatly enhance your history search experience. Here's how you can set it up:

Install fzf: You can find installation instructions for fzf on its GitHub repository (https://github.com/junegunn/fzf). Follow the installation steps specific to your operating system.

Configure fzf in your ~/.zshrc file: Open the ~/.zshrc file in a text editor and add the following line at the end of the file:

source ~/.fzf.zsh

This line ensures that fzf is sourced every time you start a new zsh session.

Save the changes and open a new terminal tab or restart your terminal for the changes to take effect.

To search history with the text already entered at the prompt, simply press Ctrl+R as before. However, this time, the fzf utility will be activated, providing an interactive search experience.

Start typing the command or text you want to search for, and fzf will filter the history based on your input in real-time. You can use the Up and Down arrow keys to navigate through the search results. Press Enter to execute the selected command, or press Ctrl+C to cancel the search and return to the prompt without executing any command.

By integrating fzf into your zsh configuration, you'll have a powerful and efficient way to search your command history, even when you've already entered text at the prompt.

Open a new tab and press CTRL+R

You should see the following:

enter image description here

1
  • As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
    – Community Bot
    May 27, 2023 at 19:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .