Mantenga solo comandos exitosos en el historial de BASH

20

A veces no entiendo la sintaxis de un comando:

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

Lo intento de nuevo y lo hago bien:

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

¿Cómo evito que el primer comando, con un código de error diferente a 0, ingrese al historial?

Adam Matan
fuente

Respuestas:

20

No creo que realmente quieras eso. Mi flujo de trabajo habitual es el siguiente:

  • Escribe un comando
  • Ejecutarlo
  • Nota que falla
  • Presione la tecla ARRIBA
  • Edite el comando
  • Ejecútalo de nuevo

Ahora, si el comando fallido no se guardara en el historial, no podría recuperarlo fácilmente para corregirlo y ejecutarlo nuevamente.

Rene Saarsoo
fuente
3
Supongo que un mejor diseño sería un historial de sesión y un historial permanente. ¡Gracias!
Adam Matan
El historial se guarda al salir de la terminal. Entonces, si bien puede volver a los comandos que ha escrito en esta sesión de terminal, en realidad se guarda en el historial de bash al salir de la terminal.
Para hacer
11

La única manera que puedo pensar para hacer esto sería utilizar history -den $PROMPT_COMMAND. El problema con este o cualquier enfoque es que es imposible saber si un comando salió con un error o se completó con éxito con un código de salida distinto de cero.

$ grep non_existent_string from_file_that_exists
$ echo $?
1
Pausado hasta nuevo aviso.
fuente
4

Es bueno tener el último comentario incorrecto para corregirlo, pero poco después se convierte en basura potencialmente confusa.

Mi enfoque es de dos pasos: almacenar comandos que fallan cuando lo hacen y eliminarlos en algún momento posterior.

Almacenar comandos que fallan cuando lo hacen:

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

trap error_handler ERR

trap command signalsse ejecuta commandcuando uno de ellos signalsestá "elevado".

$(command), ejecuta commandy captura su salida.

Cuando el comando falla, este fragmento de código captura el número de historial del último comando guardado en el historial y lo almacena en una variable para su eliminación futura.

Simple, pero funciona incorrectamente con HISTCONTROLy HISTIGNORE: cuando el comando no se guarda en el historial debido a una de las variables, el número de historial del último comando guardado en el historial es el comando anterior; entonces, si un comando incorrecto no se guarda en el historial, el comando anterior se eliminará.

Versión un poco más complicada, que funciona correctamente en ese 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

Elimine los comandos almacenados más tarde:

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

trap exit_handler EXIT

Explicación:

Al salir de Bash, para cada número de historial único, elimine la entrada de historial correspondiente,
luego borre FAILED_COMMANDSpara no eliminar comandos que heredaron números de historial de comandos ya eliminados.

Si está seguro de que FAILED_COMMANDSestará libre de duplicados, puede iterar sobre él
(es decir, escribir for i in $FAILED_COMMANDS). Sin embargo, si espera que no se ordene de mayor a menor (en este caso, siempre lo es), reemplácelo uniqcon sort -rnu.

Los números de historial FAILED_COMMANDSdeben ser únicos y ordenados de mayor a menor, porque cuando elimina la entrada, los números de los siguientes comandos se desplazan, es decir. cuando emite history -d 2, la tercera entrada se convierte en la segunda, la cuarta se convierte en la tercera, etc.

Debido a que, al utilizar este código, no se puede llamar de forma manual history -d <n>
, donde nes menor o igual al mayor número almacenado enFAILED_COMMANDS
y esperar que el código funcione correctamente.

Es probablemente una buena idea conectar exit_handlera EXIT, pero también se puede llamar en cualquier momento anterior.

GingerPlusPlus
fuente