124

I recently switched from bash to zsh. In bash, one way (besides recursive search) that I used to find previously-run commands was history | grep whatever, where whatever is the bit of command I remember.

In zsh, this isn't working. history returns only a few items, even though my .zsh_history file contains many entries, which I have configured it to do.

How can I output my whole history, suitable for searching with grep?

Nathan Long
  • 26,565

4 Answers4

173

History accepts a range in zsh entries as [first] [last] arguments, so to get them all run history 0.

To get the zsh help (at least with mind) type Alt-h over the history command and this will bring up the help for built-ins.

Kyle Brandt
  • 4,529
43

The accepted answer is correct, but it’s worth noting that you don’t need to call the external grep binary to do the search, since that ability is baked in. I have this function defined in my .zshrc:

histsearch() { fc -lim "*$@*" 1 }

Notes:

  • fc is the zsh builtin that controls the interactive history. history is equivalent to fc -l.

  • The -m flag requires a pattern, which must be quoted.

  • The -i flag adds a timestamp.

  • fc has many more tricks up its sleeve (e.g. limiting the search to internal history for the current session). See the zshbuiltins(1) man page or the official documentation.

Edit (2021-01-27):

A major advantage of using this method over just grepping the zsh history file is you get human-readable timestamps via -i. Of course, this only works if you’ve enabled the saving of timestamps to the history file in the first place:

setopt EXTENDED_HISTORY

Over the years, I’ve also added the -D flag to my function, which shows the runtime of the command in history. (This is again dependent on EXTENDED_HISTORY.) Plus, I’ve renamed the function to hgrep, which I find easier to remember:

hgrep () { fc -Dlim "*$@*" 1 }
wjv
  • 1,081
  • 3
    Better yet is to use "*$@*" (note additional stars) pattern instead of "$@" as the latter yields only exact matches. – Piotr Dobrogost May 07 '18 at 11:50
  • 2
    You’re completely right, @PiotrDobrogost! I’m not sure how I managed to submit this answer in the state that I did — I’m guessing I must’ve typed that function from memory. As it stands it’s not very usable, so I’m going to edit the answer to incorporate the asterisks. – wjv May 07 '18 at 13:15
  • 1
    I just switched to zsh from bash, this helped me out. I wished I had this tip years ago. All that time I've been foolishly using grep. – Halfstop Apr 24 '20 at 13:53
  • Using grep is certainly not "foolish", @Halfstop. In fact, I would say it's the obvious thing to do, and probably how most people would accomplish this task daily. My answer is more of a "zsh insider alternative". (But it turns out that it does have certain advantages, as I pointed out in the edit I made to the answer in January this year.) – wjv Apr 06 '21 at 08:04
  • The 2nd quote in the last command is probably a typo. – Teddy C Sep 09 '21 at 07:01
  • @TeddyC Yeah I have no idea how that crept in. Fixed; thanks for pointing it out. – wjv Sep 10 '21 at 09:13
  • if you’re calling it hgrep, wouldn't it be better to write it something like hgrep() { fc -Dli 1 | grep $@ }? EDIT: I tried this and it’s quite slow, compared to other methods. Must be decoding -Di before feeding to grep. – Rick Jul 04 '23 at 22:53
7

Have a look at fzf. It helps not only finding "whatever-particles" in your shell history, but also in other interesting places, e.g. browser history, directory history, etc.

fzf is a command-line fuzzy finder. That means you can search for particles or fractions of what you are looking for and it will display a collection of matches which you can continuously refine. It's really a game changer.

The homepage of the author contains a number of illustrative examples.

0

Using FZF with History

Including the suggestion from other threads here I found the best way (for me at least) to achieve searching my history using fzf.

  1. Ensure you have already installed fzf first, also you will need bat.
  2. Configure your fzf as (put this in your alias)
 #########################################################
#             CHECK THE BINARY OF FZF
# URL: https://github.com/Homebrew/brew/blob/master/docs/\
# FAQ.md#why-is-the-default-installation-prefix-opthomebrew-on-apple-silicon
#########################################################
case $(uname -s) in
"Darwin")
  if [ "$(uname -m)" = "arm64" ]; then
    fzf_binary="/opt/homebrew/bin/fzf"
  elif [ "$(uname -m)" = "x86_64" ]; then
    fzf_binary="/usr/local/bin/fzf"
  fi
  ;;
"Linux")
  if [ "$(uname -m)" = "x86_64" ]; then
    fzf_binary="/home/linuxbrew/.linuxbrew/bin/fzf"
  fi
  ;;
esac

alias fzf="$fzf_binary --preview-window top:30 --preview '([[ -f {} ]] && (bat --style=numbers --color=always
{} || bat {})) || ([[ -d {} ]] && (tree -C {} | less)) || echo {} 2> /dev/null | head -200'"

  1. Pip the history 0 to the fzf using
history 0 | fzf

You should be able smoothly to find any command in the past enter image description here

Dr Neo
  • 101