bash_history: comente comandos perigosos: `#`

bash_history: comente comandos perigosos: `#`

Para evitar o registro de comandos "perigosos" no histórico do bash, adicionei a seguinte linha ao meu .bashrcarquivo:

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

isso funciona bem, mas tem um efeito colateral: não consigo ver o histórico completo dos comandos executados em uma máquina. Digamos que eu tenha várias máquinas para experimentos e queira ver todos os comandos executados. Eu usaria o bash interno historypara exibir os comandos executados e talvez o grep para a data de hoje:

history | grep Sep-28

O que eu gostaria é registrar também comandos "perigosos", mas colocar um #no início da linha, para que, se eu executar o comando do histórico por engano, nenhum dano seja causado.

Não tenho ideia se isso é possível.

Atualização e esclarecimento:

A principal razão pela qual isso é um problema para mim é que normalmente estou conectado à minha máquina a partir de vários terminais, e qualquer comando executado em um terminal é imediatamente lido no histórico de outros terminais. Isto é conseguido por

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

Vamos imaginar que tenho dois terminais abertos. Em um deles, tenho algum cat /dev/foo > file.outprocesso em execução. No segundo, verifico o progresso com ls -lAhF. Continuo repetindo lspressionando Upe ENTER(ou seja, último comando do histórico). Assim que o primeiro comando terminar, o último comando do histórico não será mais ls, mas cat /dev/foo > file.out. Se eu não tomar cuidado, iniciarei o cat novamente e substituirei o file.out.

O que eu gostaria de conseguir é que o comando cat fosse precedido de a #, para que não fosse executado. No entanto, eu ainda o veria no histórico e poderia reutilizá-lo (se for um comando longo) descomentando-o.

Responder1

Não vou responder exatamente à sua pergunta, mas talvez lhe dê uma solução alternativa para o seu problema.

Se bem entendi, você está preocupado com os erros que pode cometer ao digitar, por exemplo, !rmse acontecer de o rmcomando anterior no histórico remover algo que você gostaria de manter.

Neste caso, um belofestaopção é histverify. Se você shopt -s histverify, e se você lembrar de um comando com o bang !, ele não será executado imediatamente, mas será carregado na linha de leitura para que você possa decidir executá-lo ou não, e isso também lhe dá a possibilidade de editá-lo.

Tente:

  • Sem 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
    
  • Com histverify:

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

    Neste caso você terá o cursor no final da linha, pronto para iniciá-la novamente - ou não - ou para editá-la.

Se você gosta desta opção, coloque-a em seu .bashrc:

shopt -s histverify

Responder2

Você poderia fazer 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

A ideia é que antes de cada prompt, verifiquemos a última entrada do histórico (history 1 ) e se é uma dasperigosouns, nós o excluímos ( history -d) e o adicionamos de volta com um #with history -s.

(obviamente, você precisa remover sua HISTIGNOREconfiguração).

Um efeito colateral indesejado disso é que altera otempo da históriadesses rm, mv... comandos.

Para corrigir isso, uma alternativa poderia 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

Desta vez, registramos a hora do último histórico e, para adicionar de volta a linha do histórico, usamos history -rum arquivo temporário (o documento aqui) que inclui o carimbo de data/hora.

Você gostaria que o fixhistfosse executado antes do seu history -a; history -c; history -r. Infelizmente, a versão atual do bashtem um bug que history -anão salva aquela linha extra que adicionamos. Uma solução alternativa é escrevê-lo:

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

Isso significa anexar o comando comentado ao HISTFILE em vez de deixar history -afazer isso.

Responder3

Oresposta aceita de Stéphaneé absolutamente incrível. Abaixo está minha adoção, o que faz com que o padrão corresponda um pouco diferente.

Usar ifinstruções em vez de caseme permite usar correspondência de regex ( [[ "$cmd" =~ <REGEX> ]]), o código abaixo funciona pela seguinte lógica:

  • Não adicione ao histórico no seguinte caso:
    • A linha de comando começa ou termina com 2 ou mais espaços consecutivos
      Exemplo:
      • <spc><spc>history
  • Adicionar comando comentado no seguinte caso:
    • rmou mvcomandos com interruptores cli
    • A linha de comando começa com :;ou um espaço único ou termina com ;:
      Exemplos:
    • rm -R /tmp/somedir

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

      Isso também tem advertências, com as quais concordo, por exemplo:

    • echo ";rm -something"

  • Caso contrário, adicione ao histórico
    . Exemplos:
    • rm somefile
    • echo ";rm -something"

O 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"

informação relacionada