Mantenga solo los comandos exitosos en el historial de BASH

Mantenga solo los comandos exitosos en el historial de BASH

A veces entiendo mal 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 código de error diferente a 0, ingrese al historial?

Respuesta1

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
  • Editar el comando
  • Ejecútelo de nuevo

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

Respuesta2

La única forma que se me ocurre de hacer esto sería utilizar history -din $PROMPT_COMMAND. El problema con este o cualquier enfoque es que es imposible saber si un comando salió con un error o se completó exitosamente con un código de salida distinto de cero.

$ grep non_existent_string from_file_that_exists
$ echo $?
1

Respuesta3

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

Mi enfoque consta de dos pasos: almacenar los comandos que fallan cuando lo hacen y eliminarlos más tarde.

Almacene los 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 signalses "elevado".

$(command), ejecuta commandy captura su salida.

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

Simple, pero funciona incorrectamente con HISTCONTROLyHISTIGNORE – cuando el comando no se guarda en el historial debido a una de las variables,número de historial del último comando guardado en el historiales el del comando anterior; por lo tanto, si el 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 adelante:

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 del historial correspondiente,
luego borre FAILED_COMMANDSpara no eliminar los comandos que heredaron números de historial de comandos ya eliminados.

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

Los números del historial FAILED_COMMANDSdeben ser únicos y estar ordenados de mayor a menor, porque cuando elimina una 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.

Por eso, al utilizar este código, no puede llamar manualmente history -d <n>
a dónde nes menor o igual que el mayor número almacenadoFAILED_COMMANDS
y esperar que el código funcione correctamente.

Probablemente sea una buena idea conectarte exit_handlera EXIT, pero también puedes llamarlo en cualquier momento antes.

información relacionada