Zsh History – How to Comment Out Dangerous Commands

command historyzsh

In my related post many years ago, I found a solution how to comment out "dangerous" commands saved in bash history, so that I do not execute them accidentally.

What would be the best solution to implement the same in zsh ?

Does zsh provide some functionality which I could use for this purpose?
I assume, zsh beieng more flexible, this should be easier in zsh.

For reference, this is what I have been using in bash (based on accepted answer from Stéphane Chazelas):

fixhist() {
   local cmd time histnum
   cmd=$(HISTTIMEFORMAT='<%s>' history 1)
   histnum=$((${cmd%%[<*]*}))
   time=${cmd%%>*}
   time=${time#*<}
   cmd=${cmd#*>}
   case $cmd in
     (cp\ *|mv\ *|rm\ *|cat\ *\>*|pv\ *|dd\ *)
       history -d "$histnum" # delete
       history -a
       [ -f "$HISTFILE" ] && printf '#%s\n' "$time" " $cmd" >> "$HISTFILE";;
     (*)
       history -a
   esac
   history -c
   history -r
}

UPDATE:

While the accepted solution works, it has undesired side effects. In particular, the following history options specified in zshrc are now being ignored

setopt histignorespace
setopt histreduceblanks

How can I make them work again ?

Best Answer

Sure, use the zshaddhistory hook function and disable regular history handling.

function zshaddhistory() {
  # defang naughty commands; the entire history entry is in $1
  if [[ $1 =~ "cp\ *|mv\ *|rm\ *|cat\ *\>|pv\ *|dd\ *" ]]; then
    1="# $1"
  fi
  # write to usual history location
  print -sr -- ${1%%$'\n'}
  # do not save the history line. if you have a chain of zshaddhistory
  # hook functions, this may be more complicated to manage, depending
  # on what those other hooks do (man zshall | less -p zshaddhistory)
  return 1
}

Tested thusly on zsh 5.0.8

% exec zsh
% echo good
good
% echo bad; rm /etc 
bad
rm: /etc: Operation not permitted
% history | tail -4
  299  exec zsh
  300  echo good
  301  # echo bad; rm /etc
  302  history | tail -4
%   

This appears to work with the extendedhistory option set, as well.

Related Question