Para evitar o registro de comandos "perigosos" no histórico do bash, adicionei a seguinte linha ao meu .bashrc
arquivo:
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 history
para 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.out
processo em execução. No segundo, verifico o progresso com ls -lAhF
. Continuo repetindo ls
pressionando 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, !rm
se acontecer de o rm
comando 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 HISTIGNORE
configuraçã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 -r
um arquivo temporário (o documento aqui) que inclui o carimbo de data/hora.
Você gostaria que o fixhist
fosse executado antes do seu history -a; history -c; history -r
. Infelizmente, a versão atual do bash
tem um bug que history -a
nã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 -a
fazer 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 if
instruções em vez de case
me 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
- A linha de comando começa ou termina com 2 ou mais espaços consecutivos
- Adicionar comando comentado no seguinte caso:
rm
oumv
comandos 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"