39

Currently, I have the following in my .zshrc:

bindkey '^[[A' up-line-or-search
bindkey '^[[B' down-line-or-search

However, this only seems to match the content of my current input before a space character occurs. For example, sudo ls / will match every line in my history that begins with sudo, while I would like it to only match lines that match my entire input. (i.e. sudo ls /etc would match, but not sudo cat /var/log/messages)

What do I need to change in order to gain the desired behavior?

Here is my entire .zshrc in case it is relevant: https://gist.github.com/919566

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
rps
  • 673
  • 1
  • 6
  • 7

5 Answers5

54

zsh provide this functionality by using

history-beginning-search-backward
history-beginning-search-forward

Ex.

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

Find exact Key code by
ctrl+vKEY

Ex.
ctrl+vUP
ctrl+vDOWN
ctrl+vPageUp
ctrl+vPageDown
etc.

In case if you are using mac the below works on OSX catalina.

 bindkey "\e[5~" history-search-backward
 bindkey "\e[6~" history-search-forward
Knight71
  • 103
  • 4
mudrii
  • 736
  • 1
  • 6
  • 4
  • 1
    An alternative for keycodes that might work across different terminals can be found in zprezto \[[1](https://raw.githubusercontent.com/sorin-ionescu/prezto/28a20b48e652a01216a6c3dd76e6324d76c12def/modules/editor/init.zsh)]. Up is `$terminfo[kcuu1]`, Down is `$terminfo[kcud1]`. – Justin C Nov 22 '15 at 21:02
16

This blog post from 2013 recommends a couple of keybinds that match all the words before the cursor.

# Cycle through history based on characters already typed on the line
autoload -U up-line-or-beginning-search
autoload -U down-line-or-beginning-search
zle -N up-line-or-beginning-search
zle -N down-line-or-beginning-search
bindkey "$key[Up]" up-line-or-beginning-search
bindkey "$key[Down]" down-line-or-beginning-search

If $key[Up] and $key[Down] don't work on your machine, you could try $terminfo[kcuu1] and $terminfo[kcud1] instead.

joeytwiddle
  • 1,008
  • 9
  • 15
  • 1
    zsh also gives these friendlier aliases (at least in the ubuntu package) in `/etc/zsh/zshrc`, such as `$key[Up]`. – zzxyz Nov 17 '17 at 21:38
  • If using `$terminfo` you'll need to change the terminal modes. See [here](https://github.com/robbyrussell/oh-my-zsh/blob/3705d47bb3f3229234cba992320eadc97a221caf/lib/key-bindings.zsh#L5). – Tom Hale Jul 13 '18 at 11:33
  • after a lot of searching, this worked for me on my ubuntu vps – Alex Jones Mar 07 '21 at 06:59
12

This is the documented behavior:

down-line-or-search
Move down a line in the buffer, or if already at the bottom line, search forward in the history for a line beginning with the first word in the buffer.

There doesn't seem to be an existing widget that does exactly what you want, so you'll have to make your own. Here's how to define a widget that behaves like up-line-or-search, but using the beginning of the line (up to the cursor) rather than the first word as search string. Not really tested, especially not on multi-line input.

up-line-or-search-prefix () {
  local CURSOR_before_search=$CURSOR
  zle up-line-or-search "$LBUFFER"
  CURSOR=$CURSOR_before_search
}
zle -N up-line-or-search-prefix

An alternate approach is to use history-beginning-search-backward, but only call it if the cursor is on the first line. Untested.

up-line-or-history-beginning-search () {
  if [[ -n $PREBUFFER ]]; then
    zle up-line-or-history
  else
    zle history-beginning-search-backward
  fi
}
zle -N up-line-or-history-beginning-search
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • Thanks, Gilles, your first solution works pretty well, but there's one issue and I'm hoping you can help me resolve it. When I call up-line-or-search-prefix on an empty input, it gives me the last run command -- the desired behavior. However, when I run up-line-or-search-prefix again, it does an up-line-or-search on the first character of my input. Is it possible to somehow change things so that pressing the up arrow with an empty input will scroll through my history? – rps Jul 14 '11 at 16:33
  • @rps Ah, gotcha, it was picking up the first word because the argument was missing (instead of being empty as it should have). Use my edited version. – Gilles 'SO- stop being evil' Jul 14 '11 at 23:02
2

Have also a look at the zsh-history-substring-search project.

It offers what you are looking for, and it can even be used as a oh-my-zsh plugin.

Matthieu Napoli
  • 1,162
  • 2
  • 11
  • 14
1

I found an answer to the question here, that I think is perfect.

For example, bindkey these functions to the "Page Up" key and "Page Down" key, then when I press "Page Up" it will search through history using what I have started typing, not just the first word.

i.e. if you type sudo ls /etc then sudo ls will bring up sudo ls /etc but not sudo cat... etc.

Below is the excerpt from my zshrc.

## Page UP and Page Down through History
autoload -Uz history-search-end

zle -N history-beginning-search-backward-end \
                history-search-end
zle -N history-beginning-search-forward-end \
                history-search-end
bindkey "\e[5~" history-beginning-search-backward-end
bindkey "\e[6~" history-beginning-search-forward-end
djorborn
  • 11
  • 2