Capitalizing on the Linux commands history

Tuesday, March 9, 2021

Working with Linux I tend to forget commands that I’m not using regularly. I also don’t like to retype a long command. Fortunately, the bash history can be used to find back those previous commands.

The basic usage is to cycle through the bash history with the up and down keys, we also can use Ctrl-R to type the beginning of a command and autocomplete it according to the history.

This history of commands is actually pretty valuable, a lot of time can be saved recovering previous commands with a few keystrokes. We could use this history to type a command once and use it as a reference forever, productivity gets compounded over time!

The default bash history workflow can be greatly improved, first by modifying history limits, and second by using a more powerful history searching tool.

Improving the history retention

First, we can modify the ~/.bashrc configuration. For that, I got largely inspired by this repository.

I use the following configuration in my bashrc :

## SANE HISTORY DEFAULTS ##
# Append to the history file, don't overwrite it
shopt -s histappend
# Save multi-line commands as one command
shopt -s cmdhist
# Record each line as it gets issued
PROMPT_COMMAND='history -a'
# Huge history. Doesn't appear to slow things down, so why not?
HISTSIZE=500000
HISTFILESIZE=10000000
# Avoid duplicate entries
HISTCONTROL="erasedups:ignoreboth"
# Don't record some commands
export HISTIGNORE="&:[ ]*:exit:ls:bg:fg:history:clear"
# Use standard ISO 8601 timestamp
# %F equivalent to %Y-%m-%d
# %T equivalent to %H:%M:%S (24-hours format)
HISTTIMEFORMAT='%F %T '

I also source control my ~/.bash_history file.

When I get to use another computer, I can just pull the previous history and append it to my local .bash_history file.

This allows me to keep several years of bash history!

Using a better tool for searching history

There are various tools to search more efficiently in the commands history (fzf, mcfly). I am using hstr because I like its interface and it does everything I need. hstr basically allows to «Easily view, navigate and search your command history with shell history suggest box».

To install this tool, refer to the project README.

Here is the configuration I use for hstr, to be added in the .bashrc:

# HSTR configuration - add this to ~/.bashrc
alias hh=hstr                    # hh to be alias for hstr
export HSTR_CONFIG=hicolor,raw-history-view,blacklist,substring-matching

# ensure synchronization between Bash memory and history file
export PROMPT_COMMAND="history -a; history -n; ${PROMPT_COMMAND}"
# if this is interactive shell, then bind hstr to Ctrl-r (for Vi mode check doc)
if [[ $- =~ .*i.* ]]; then bind '"\C-r": "\C-a hstr -- \C-j"'; fi
# if this is interactive shell, then bind 'kill last command' to Ctrl-x k
if [[ $- =~ .*i.* ]]; then bind '"\C-xk": "\C-a hstr -k \C-j"'; fi

(I am not using the tool’s default configuration as it overrides HISTFILESIZE and HISTSIZE variables.)

In terms of features, hstr allows to :

  • View commands according to
    • History
    • Ranking (most used)
    • Favorites (I don’t use that but you can pin your favorite commands)
  • Search commands by
    • Match exact (commands starting with the search string)
    • Match Regexp
    • Match keywords
  • Configure case sensitivity for the search

Example of a use case

A few months ago, I had been experimenting with running a temporary docker-in-docker (dind) pod in a Kubernetes cluster to try out a few things. In order to use dind, the pod must have a privileged security context. There is actually a way to specify this security context in a cli one-liner by overriding the pod template, that gives a pretty long bash command with some json in it, not something I want to remember when I get it working.

To find this command back, I type Ctrl-R to open the hstr tool. Then I type two times Ctrl-E to search by keywords. I remember that it was a kubectl command and that I used privileged somewhere in that command, so I just need to write those two keywords.

Then, hstr succesfully finds my command back:

kubectl run -n edp tmp-shell --image=docker:18.09.6-dind --rm -i --tty --overrides='{"spec": {"containers": [{"name": "tmp-shell", "image": "docker:18.09.6-dind", "securityContext": {"privileged": true} }]}}'

… Nice and easy :-)