bash_history: comment out dangerous commands: `#`
I'm not going to exactly answer your question, but maybe give you an alternate solution to your problem.
If I understand correctly you're concerned about mistakes you could make by typing, e.g., !rm
if it happened that the previous rm
command in history removes something you'd like to keep.
In this case, a nice bash option is histverify
. If you shopt -s histverify
, and if you recall a command with the bang !
, it will not execute immediately, but be loaded in the readline so that you can either decide to execute it or not, and this also gives you the possibility to edit it.
Try it:
Without
histverify
:$ touch some_foo $ rm some_foo $ touch some_foo $ !rm rm some_foo $ # oooops in fact I'd've like to keep it this time
With
histverify
:$ shopt -s histverify $ touch some_foo $ rm some_foo $ touch some_foo $ !rm $ rm some_foo <cursor here>
In this case you'll have the cursor at the end of the line, ready to launch it again -- or not -- or to edit it.
If you like this option, put it in your .bashrc
:
shopt -s histverify
You could do something like:
fixhist() {
local cmd histnum
cmd=$(HISTTIMEFORMAT=/ history 1)
histnum=$((${cmd%%[*/]*}))
cmd=${cmd#*/} # remove the histnum
case $cmd in
(rm\ *|mv\ *|...)
history -d "$histnum" # delete
history -s "#$cmd" # add back with a #
esac
}
PROMPT_COMMAND=fixhist
The idea being that before each prompt, we check the last history entry (history 1
) and if it's one of the dangerous ones, we delete it (history -d
) and add it back with a #
with history -s
.
(obviously, you need to remove your HISTIGNORE
setting).
An unwanted side effect of that though is that it alters the history time of those rm
, mv
... commands.
To fix that, an alternative could be:
fixhist() {
local cmd time histnum
cmd=$(HISTTIMEFORMAT='<%s>' history 1)
histnum=$((${cmd%%[<*]*}))
time=${cmd%%>*}
time=${time#*<}
cmd=${cmd#*>}
case $cmd in
(rm\ *|mv\ *|...)
history -d "$histnum" # delete
HISTFILE=/dev/stdin history -r <<EOF
#$time
#$cmd
EOF
esac
}
PROMPT_COMMAND=fixhist
This time, we record the time of the last history, and to add back the history line, we use history -r
from a temporary file (the here document) that includes the timestamp.
You'd want the fixhist
to be performed before your history -a; history -c; history -r
. Unfortunately, the current version of bash
has a bug in that history -a
doesn't save that extra line that we've added. A work around is to write it instead:
fixhist() {
local cmd time histnum
cmd=$(HISTTIMEFORMAT='<%s>' history 1)
histnum=$((${cmd%%[<*]*}))
time=${cmd%%>*}
time=${time#*<}
cmd=${cmd#*>}
case $cmd in
(rm\ *|mv\ *|...)
history -d "$histnum" # delete
history -a
[ -f "$HISTFILE" ] && printf '#%s\n' "$time" "$cmd" >> "$HISTFILE";;
(*)
history -a
esac
history -c
history -r
}
PROMPT_COMMAND=fixhist
That is to append the commented command to the HISTFILE ourselves instead of letting history -a
do it.