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.
Understanding related environment variables
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 fileHISTSIZE
- Refers to the number of commands that are loaded into memory from the history fileHISTFILE
- 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
# In ~/.zshrc
export HISTFILESIZE=
export HISTSIZE=
export HISTFILE=~/.zsh_history
setopt HIST_FIND_NO_DUPS
# following should be turned off, if sharing history via setopt SHARE_HISTORY
setopt INC_APPEND_HISTORY
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.