Better zsh history


misc zsh shell command-line

In a previous article - Unlimited Bash History, we discussed how to have a bigger(or unlimited) history for bash. This post is a follow-up on on that, and we will try to replicate the same for zsh. Let’s try to understand the different environment variables involved and tweak them to have a bigger(or unlimited) history. It’s also worth pointing out that bash and zsh have different default configurations with respect to history, and also the names of the history-related environment variables are also different between them.


Why do we need more history?

As developers, we find ourselves trying to remember a certain command we used in the past (which is pretty lengthy to type or uses a specific combination of options/arguments). The reverse history search(ctrl-r) feature comes in very handy in such situations. This command lets you search through the history of commands stored in the history file.

By default, zsh does not save the history to a file - This is not ideal since we will lose all our history once we exit a shell and there is no way to search/re-use previously used commands. By saving history to a file, and by letting the file grow very large, more commands can be retained and be available for searching.


Before we get started, let’s try to understand a few environment variables pertaining to history in zsh.

  • SAVEHIST - Refers to the number of commands that are stored in the zsh history file

  • HISTSIZE - Refers to the number of commands that are loaded into memory from the history file

  • HISTFILE - Refers to the path/location of the history file

By default, these variables are not set, and history is not persisted to a file. However, if you are using plugins like Oh-my-zsh, some of these variables may have been set already. If you would like to know more about the different options available, do check out the man page for zshoptions (Try man zshoptions from the commandline) or from the Zsh documentation online.


Steps to setup zsh to have unlimited history


1. Set the zsh history file

As we figured out earlier, zsh does not save history to a file by default. We need to set the HISTFILE environment variable for that to happen. (If you are using plugins like zsh, search if the variable is already set by typing echo $HISTFILE on the commandline - If set, you can choose to retain it and skip this step)

# In ~/.zshrc
export HISTFILE=~/.zsh_history

2. Increase history size

In bash, Setting the HISTFILESIZE and HISTSIZE variables to an empty string makes the bash history size unlimited. However, it’s not possible to set the history to an unlimited size in zsh(theoretically, at least). From an zsh mailing list, it appears the max history size can be LONG_MAX from limits.h header file. That has a (really huge) value of 9223372036854775807, which should be enough to store trillions of commands (a limit we’ll probably never hit). This number can be hard to remember - We can just set this to a billion, and forget it.

# In ~/.zshrc
export HISTFILESIZE=1000000000
export HISTSIZE=1000000000

Some nice-to-haves

Now that we have increased the size of our history, let’s try to make it more robust (And that means meddling with more environment variables!).

1. Immediate append Setting the inc_append_history option ensures that commands are added to the history immediately (otherwise, this would happen only when the shell exits, and you could lose history upon unexpected/unclean termination of the shell).

# In ~/.zshrc
setopt INC_APPEND_HISTORY
export HISTTIMEFORMAT="[%F %T] "

2. Add Timestamp to history

setopt EXTENDED_HISTORY

Setting the extended_history records the timestamp of each command in HISTFILE (along with the actual command) You can later find out the exact time of execution for a command, if needed. Also, using the history command to view the last n commands displays the timestamp information, in addition to the actual command:

# On the command line
$ history -E -10
  474  4.12.2019 00:08  unsetopt | grep extended
  475  4.12.2019 00:08  history 5
  476  4.12.2019 00:08  history -E
  477  4.12.2019 00:09  man zsh
  478  4.12.2019 00:09  man zshoptions | less -p EXTENDED_H
  479  4.12.2019 00:10  cat ~/.zsh_history
  480  4.12.2019 00:11  history -D
  481  4.12.2019 00:11  history -E
  482  4.12.2019 00:11  man history
  483  4.12.2019 00:12  history -10

3. Handling duplicate commands

(While searching with Ctrl+R) Stepping through the history with UP and DOWN keys becomes a bit annoying if the same command comes up again and again. A better option is to skip duplicates and show each command only once with the following setting. (Duplicate commands are still written to the history - However, they come up just once while stepping through).

# In ~/.zshrc
setopt HIST_FIND_NO_DUPS

We can take it up a notch by not writing duplicates to the history file at all, using the HIST_IGNORE_ALL_DUPS option.

# In ~/.zshrc
setopt HIST_IGNORE_ALL_DUPS

This ensures all previous lines matching the current command is removed from history before the current command is saved.


Putting everything together


I use oh-my-zsh. Should I bother with any of this?

Probably not. oh-my-zsh already already does most of the options discussed above (and lots of other options as well ) - Have a look here, if you’d like to understand the specifics. I feel the SAVEHIST value could be bigger than the 10000 oh-my-zsh uses, but everything else is great out-of-the-box.


Conclusion

In this post, we saw how to make our zsh history bigger, so that searching for past commands is seamless. Personally, I didn’t think there was so much to zsh history(Boy, was I wrong!), and learnt a lot of things while setting this up - I hope you found the post useful too. See you in the next post.


See Also