Super safe bash history

Ayush Goyal
3 min readNov 23, 2020

--

A catastrophe is just a few keystrokes away between that Ctrl-r and Returnon shell.

Intro

If you are like me, someone who spends a lot of time on the terminal, often ssh’d into a remote server, you start appreciating the power of scrolling through your bash history via Ctrl-r. It saves a ton of time from typing commonly used commands, quite the life saver.

A few years back though, this nifty workflow ended up biting me in the ass. While scrolling through my command history, I pressed a sequence of keystrokes (by mistake), which ended up executing a DROP TABLEon Mysql.
It happened so fast, I didn’t realize what hit me.

Luckily, it was a non-critical monitoring system, and affected the Grafana dashboard db to be precise. It took a couple of days worth effort, to reconstruct those dashboards. Sure enough, we added the missed mysqldump scripts on that server the next day.

In aftermath of this incident, I spent some time thinking how to avoid this from happening in future. Lot of sysadmins, rely on their bash history to quickly get stuff done. And no matter how much seasoned touch typing skills you possess, it’s not without some amount of error rate. A catastrophe is just a few keystrokes away between that Ctrl-r and Return on shell. That Murphy’s law ain’t forgiving.

Solution

Thinking about it, a simple idea came to me. Why not comment all the commands in bash history after each command execution?

This nifty idea is super simple to implement by adding following lines in your ~/.bash_profile:

function make_bash_history_safe() {
history -a # Save current history in memory to ~/.bash_history
history -c # Clear current history list in memory
# Comment all uncommented commands in ~/.bash_history with sed
sed -i -e 's/^\([^#]\)/# \1/g' ~/.bash_history
history -r # Reload history in memory from ~/.bash_history file
}
# Execute make_bash_history_safe after each command execution
PROMPT_COMMAND="make_bash_history_safe; "

I am quite sure this wasn’t/isn’t an original idea, I must have copied this snippet from somewhere on stack-overflow. But it has been with me for a while in various incarnations.

Result

A screencast of safe history after each command execution

I even keep a simple snippet of make_bash_history_safe, in my snippet manager Dash. In it’s concise form, it looks like this:

history -a; history -c; sed -i -e 's/^\([^#]\)/# \1/g'  ~/.bash_history ; history -r;

After executing any unsafe command on less commonly ssh’d server, where I don’t maintain ~/.bash_profile, I run the above snippet.

Slight Pitfall

With this, the bash history is now super safe. However, to execute any command from the history, you will now require to remove the comment character # from the beginning of line. This can get slightly frustrating, but it’s just matter of pressing:

<Ctrl-a> ; Go to beginning of line
<Ctrl-d> ; Delete first character on line, `#` in this case
<Return> ; Run the command

I personally prefer delegating this to another function. Did you know, you can modify the currently typed command on bash prompt by modifying READLINE_LINE variable. With following snippet in ~/.bash_profile:

function remove_comment() {
READLINE_LINE=`echo ${READLINE_LINE} | sed -E -e "s/^[# ]+//g"`
}
bind -x '"\C-x\C-x": remove_comment'

I bind Ctrl-x Ctrl-x(Ctrl-x, pressed twice), to run the remove_comment function. As labeled, it removes any comment character #and any spaces, at the beginning of line of current command in bash prompt. After that, you can execute the command by pressing Return.

Outro

Being server admins, we are bound to run commands which can have disastrous outcomes when run in wrong environment/directory at wrong time. However minuscule the possibility might be to execute a bad command from history, I personally would err on side of caution, eliminating it using good software-assisted workflows.

There might be better workflows out there to address the same, let me know in the comments if I am missing out on anything.

Update 1: You can follow the reddit discussion on r/commandline. There are some improvements suggested by u/geirha for BSD and other environments.

--

--