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

Intro

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

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

Image for post
Image for post
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

<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

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.

Written by

Linux Geek and Anime Freak

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store