bash_history: comenta comandos peligrosos: `#`

bash_history: comenta comandos peligrosos: `#`

Para evitar registrar comandos "peligrosos" en el historial de bash, agregué la siguiente línea a mi .bashrcarchivo:

HISTIGNORE='rm *:mv *:cp *:cat*>*:pv*>*'

Esto funciona bien, pero tiene un efecto secundario: no puedo ver el historial completo de comandos ejecutados en una máquina. Digamos que tengo varias máquinas para realizar experimentos y quiero poder ver todos los comandos ejecutados. Usaría bash internal historypara mostrar los comandos ejecutados y quizás grep para la fecha de hoy:

history | grep Sep-28

Lo que me gustaría tener es registrar también los comandos "peligrosos", pero poner un #al principio de la línea, de modo que si ejecuto el comando del historial por error, no se produzca ningún daño.

No tengo idea si esto es posible.

Actualización y aclaración:

La razón principal por la que esto es un problema para mí es que normalmente estoy conectado a mi máquina desde varios terminales, y cualquier comando ejecutado en un terminal se lee inmediatamente en el historial de otros terminales. Esto se logra mediante

PROMPT_COMMAND="history -a; history -c; history -r"

Imaginemos que tengo dos terminales abiertas. En uno tengo algún cat /dev/foo > file.outproceso ejecutándose. En el segundo, compruebo el progreso con ls -lAhF. Sigo repitiendo lspresionando Upy ENTER(es decir, último comando del historial). Tan pronto como finaliza el primer comando, el último comando del historial ya no es ls, sino cat /dev/foo > file.out. Si no tengo cuidado, iniciaré cat nuevamente y sobrescribiré file.out.

Lo que me gustaría lograr es que el comando cat esté precedido por un #, para que no se ejecute. Sin embargo, todavía lo vería en el historial y puedo reutilizarlo (si es un comando largo) descomentándolo.

Respuesta1

No voy a responder exactamente a tu pregunta, pero tal vez te dé una solución alternativa a tu problema.

Si entiendo correctamente, le preocupan los errores que podría cometer al escribir, por ejemplo, !rmsi sucede que el rmcomando anterior en el historial elimina algo que le gustaría conservar.

En este caso, un bonitointentola opción es histverify. Si shopt -s histverifyrecuerdas un comando con el bang !, no se ejecutará inmediatamente, sino que se cargará en la línea de lectura para que puedas decidir ejecutarlo o no, y esto también te da la posibilidad de editarlo.

Intentalo:

  • Sin 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
    
  • Con histverify:

    $ shopt -s histverify
    $ touch some_foo
    $ rm some_foo
    $ touch some_foo
    $ !rm
    $ rm some_foo <cursor here>
    

    En este caso, tendrás el cursor al final de la línea, listo para ejecutarlo nuevamente (o no) o editarlo.

Si te gusta esta opción, ponla en tu .bashrc:

shopt -s histverify

Respuesta2

Podrías hacer algo como:

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

La idea es que antes de cada mensaje, verifiquemos la última entrada del historial ( history 1) y si es una de laspeligrosounos, lo borramos ( history -d) y lo volvemos a agregar con un #with history -s.

(Obviamente, debes eliminar tu HISTIGNOREconfiguración).

Sin embargo, un efecto secundario no deseado de esto es que altera latiempo de la historiade esos rm, mv... comandos.

Para solucionarlo, una alternativa podría ser:

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

Esta vez, registramos la hora del último historial y, para volver a agregar la línea del historial, utilizamos history -run archivo temporal (el documento aquí) que incluye la marca de tiempo.

Desea que fixhistse realice antes que su history -a; history -c; history -r. Desafortunadamente, la versión actual bashtiene un error que history -ano guarda la línea adicional que agregamos. Una solución alternativa es escribirlo en su lugar:

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

Eso es agregar el comando comentado al HISTFILE nosotros mismos en lugar de dejar que history -alo hagamos.

Respuesta3

Elrespuesta aceptada de Stéphanees absolutamente asombroso. A continuación se muestra mi adopción, que hace que la combinación de patrones sea un poco diferente.

Usar ifdeclaraciones en lugar de caseme permite usar coincidencias de expresiones regulares ( [[ "$cmd" =~ <REGEX> ]]), el siguiente código funciona con la siguiente lógica:

  • No agregue al historial en el siguiente caso:
    • La línea de comando comienza o termina con 2 o más espacios consecutivos
      Ejemplo:
      • <spc><spc>history
  • Agregar comando comentado en el siguiente caso:
    • rmo mvcomandos con interruptores cli
    • La línea de comando comienza con :;o un espacio simple o termina con ;:
      Ejemplos:
    • rm -R /tmp/somedir

    • echo "doing something dangerous I don't want to trip by accident";:

      Esto también tiene cavieats, con los que estoy de acuerdo, por ejemplo:

    • echo ";rm -something"

  • De lo contrario, agregar al historial
    . Ejemplos:
    • rm somefile
    • echo ";rm -something"

El código

_history_hook() {
# The code below is from https://unix.stackexchange.com/a/110056/138012
# history builtiun options used:  https://www.gnu.org/software/bash/manual/html_node/Bash-History-Builtins.html
# history -a : Append new (mem) lines to history (file)
# history -d : Delete range (mem)
# history -c : Clear history list (mem) ++
# history -r : Read (file) append to (mem) ++
# ++ needed to circumvent a bug issue.
   local cmd time histnum
   cmd=$(HISTTIMEFORMAT='<%s>' history 1)
   histnum=$((${cmd%%[<*]*}))
   time="${cmd%%>*}"
   time=${time#*<}
   cmd="${cmd#*>}"
   # different behaviors for different patterns
   if [[ "$cmd" =~ ^\ \ +.*|\ +\ $ ]]; then
   # delete only (two spaces - leading or trailing)
       history -d "$histnum" # delete
   elif [[ "$cmd" =~ ((^|;[[:space:]]*)(rm|mv)\ +-)|^\ |^:\;|\;:$ ]]; then
   # rewrite history as a 'safe version'
       history -d "$histnum" # delete
       history -a
       [ -f "$HISTFILE" ] && printf '# %s\n' "$time" "$cmd" >> "$HISTFILE"
   else
   # append 'as usual'
       history -a
   fi
   # rewriting history file
   history -c
   history -r
}

export HISTCONTROL=erasedups # This does not seem to work right now - need to fix this
export PROMPT_COMMAND="$PROMPT_COMMAND; _history_hook"

información relacionada