Mantenha apenas comandos bem-sucedidos no histórico do BASH

20

Às vezes eu entendo mal a sintaxe de um comando:

# mysql -d test
mysql: unknown option '-d'
# echo $?
2

Tento novamente e acerto:

# mysql --database test
Welcome to the MySQL monitor.
mysql >
...

Como evito que o primeiro comando, com código de erro diferente de 0, entre no histórico?

Adam Matan
fonte

Respostas:

20

Eu não acho que você realmente queira isso. Meu fluxo de trabalho usual é assim:

  • Digite um comando
  • Executá-lo
  • Observe que está falhando
  • Pressione a tecla UP
  • Edite o comando
  • Execute-o novamente

Agora, se o comando com falha não fosse salvo no histórico, não seria possível recuperá-lo facilmente para corrigir e executar novamente.

Rene Saarsoo
fonte
3
Eu acho que um design melhor seria um histórico de sessões e um histórico permanente. Obrigado!
Adam Matan
O histórico é salvo quando você sai do terminal. Portanto, enquanto você pode voltar aos comandos digitados nesta sessão do terminal, ele é salvo no histórico do bash ao sair do terminal.
quer
11

A única maneira que eu posso pensar de fazer isso seria usar history -dno $PROMPT_COMMAND. O problema dessa ou de qualquer abordagem é que é impossível saber se um comando saiu com um erro ou foi concluído com êxito com um código de saída diferente de zero.

$ grep non_existent_string from_file_that_exists
$ echo $?
1
Pausado até novo aviso.
fonte
4

É bom ter o último comentário incorreto para corrigi-lo, mas logo depois isso se torna um lixo potencialmente confuso.

Minha abordagem é em duas etapas: armazene os comandos que falham quando o fazem e os remova algum tempo depois.

Armazene comandos que falham quando o fazem:

error_handler() {
    FAILED_COMMANDS="$(history | tail -1l | cut -c -5) $FAILED_COMMANDS"
}

trap error_handler ERR

trap command signalsexecuta commandquando um de signalsé "aumentado".

$(command), executa o command e captura sua saída.

Quando o comando falha, esse trecho de código captura o número do histórico do último comando salvo no histórico e o armazena na variável para exclusão futura.

Simples, mas funciona incorretamente com HISTCONTROLe HISTIGNORE- quando o comando não é salvo no histórico devido a uma das variáveis, o número do último comando salvo no histórico é o comando anterior; portanto, se um comando incorreto não for salvo no histórico, o comando anterior será excluído.

Versão ligeiramente mais complicada, que funciona corretamente nesse caso:

debug_handler() {
    LAST_COMMAND=$BASH_COMMAND;
}

error_handler() {
    local LAST_HISTORY_ENTRY=$(history | tail -1l)

    # if last command is in history (HISTCONTROL, HISTIGNORE)...
    if [ "$LAST_COMMAND" == "$(cut -d ' ' -f 2- <<< $LAST_HISTORY_ENTRY)" ]
    then
        # ...prepend it's history number into FAILED_COMMANDS,
        # marking the command for deletion.
        FAILED_COMMANDS="$(cut -d ' ' -f 1 <<< $LAST_HISTORY_ENTRY) $FAILED_COMMANDS"
    fi
}

trap error_handler ERR
trap debug_handler DEBUG

Remova os comandos armazenados algum tempo depois:

exit_handler() {
    for i in $(echo $FAILED_COMMANDS | tr ' ' '\n' | uniq)
    do
        history -d $i
    done
    FAILED_COMMANDS=
}

trap exit_handler EXIT

Explicação:

Ao sair do Bash, para cada número de histórico exclusivo remova a entrada do histórico correspondente
e limpeFAILED_COMMANDS para não remover comandos que herdaram números de histórico de comandos já excluídos.

Se você tem certeza de que FAILED_COMMANDSestará livre de duplicatas, pode iterar facilmente (por exemplo,
escrever for i in $FAILED_COMMANDS). Se, no entanto, você espera que não seja classificado do maior para o menor (nesse caso, sempre é), substitua uniqpor sort -rnu.

Os números do histórico FAILED_COMMANDSdevem ser únicos e classificados do maior para o menor, porque quando você exclui a entrada, os números dos próximos comandos são alterados - ie. quando você emitir history -d 2, a 3ª entrada se tornará a 2ª, a 4ª se tornará a 3ª etc.

Por esse motivo, ao usar esse código, você não pode chamar manualmente history -d <n>
onde né menor ou igual ao maior número armazenado emFAILED_COMMANDS
e esperar que o código funcione corretamente.

É provavelmente uma boa idéia para ligar exit_handlera EXIT, mas você também pode chamá-lo a qualquer hora mais cedo.

GingerPlusPlus
fonte