7

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 ?

4

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.

  • what exactly do you mean by "disable regular history handling" ? I have following options set in my .zshrc: sharehistory histignorespace histreduceblanks extended_history inc_append_history interactive_comments – Martin Vegter Sep 28 '16 at 6:31
  • Regular history handling is typically to write the history line into the history, unaltered. With the alterations here, this would result in two history entries, one commented out, and one not. This is probably not what you want, so disable regular history handling by returning 1 from the function. – thrig Sep 28 '16 at 13:50
  • thanks it works. But it breaks some other history functions, such as: setopt histignorespace and setopt histreduceblanks. – Martin Vegter Oct 2 '16 at 12:10
  • So what happens with a command like socat or ncat? I'm not familiar with zsh, but the above seems to me like it would comment those out too. – mtraceur Oct 2 '16 at 16:08
1

The following function is based on the thrig's one and it fixes histignorespace:

function zshaddhistory() {
  if [[ $1 =~ "^ " ]]; then
    return 0
  elif [[ $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
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.