Conserver uniquement les commandes réussies dans l'historique BASH

20

Parfois, je comprends mal la syntaxe d'une commande:

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

J'essaye encore et je fais les choses correctement:

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

Comment empêcher la première commande, avec un code d'erreur différent de 0, d'entrer dans l'historique?

Adam Matan
la source

Réponses:

20

Je ne pense pas que tu veuilles vraiment ça. Mon flux de travail habituel se présente comme suit:

  • Tapez une commande
  • Exécuter
  • Remarquez qu'il échoue
  • Appuyez sur la touche HAUT
  • Modifier la commande
  • Lancez-le à nouveau

Maintenant, si la commande échouée n'était pas enregistrée dans l'historique, je ne pouvais pas la récupérer facilement pour la réparer et l'exécuter à nouveau.

René Saarsoo
la source
3
Je suppose qu'un meilleur design serait une histoire de session et une histoire permanente. Merci!
Adam Matan
L'historique est sauvegardé lorsque vous quittez le terminal. Ainsi, bien que vous puissiez revenir aux commandes que vous avez tapées dans cette session de terminal, elles sont en fait enregistrées dans l'historique bash à la sortie du terminal.
À faire le
11

La seule façon de penser à cela serait d'utiliser history -ddans $PROMPT_COMMAND. Le problème avec ceci ou n'importe quelle approche est qu'il est impossible de dire si une commande s'est terminée avec une erreur ou s'est terminée avec succès avec un code de sortie non nul.

$ grep non_existent_string from_file_that_exists
$ echo $?
1
En pause jusqu'à nouvel ordre.
la source
4

Il est bon d'avoir le dernier commentaire incorrect pour le corriger, mais peu de temps après, cela devient des ordures potentiellement déroutantes.

Mon approche est en deux étapes: stocker les commandes qui échouent lorsqu'elles le font et les supprimer un peu plus tard.

Stockez les commandes qui échouent lorsqu'elles le font:

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

trap error_handler ERR

trap command signalss'exécute commandlorsque l'un des signalsest "levé".

$(command), exécute commandet capture sa sortie.

Lorsque la commande échoue, cet extrait de code capture le numéro d'historique de la dernière commande enregistrée dans l'historique et le stocke dans une variable pour une suppression future.

Simple, mais ne fonctionne pas correctement avec HISTCONTROLet HISTIGNORE- lorsque la commande n'est pas enregistrée dans l'historique en raison de l'une des variables, le numéro d'historique de la dernière commande enregistrée dans l'historique est celui de la commande précédente; ainsi, si une commande incorrecte n'est pas enregistrée dans l'historique, la commande précédente va être supprimée.

Version légèrement plus compliquée, qui fonctionne correctement dans ce cas:

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

Supprimez les commandes stockées un peu plus tard:

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

trap exit_handler EXIT

Explication:

Lorsque vous quittez Bash, pour chaque numéro d'historique unique, supprimez l'entrée d'historique correspondante,
puis désactivez FAILED_COMMANDSpour ne pas supprimer les commandes qui ont hérité des numéros d'historique des commandes déjà supprimées.

Si vous êtes sûr qu'il FAILED_COMMANDSne contiendra pas de doublons, vous pouvez simplement le répéter
(c'est-à-dire écrire for i in $FAILED_COMMANDS). Si, cependant, vous vous attendez à ce qu'il ne soit pas trié du plus grand au plus petit (dans ce cas, il l'est toujours), remplacez uniqpar sort -rnu.

Les numéros d'historique FAILED_COMMANDSdoivent être uniques et triés du plus grand au plus petit, car lorsque vous supprimez l'entrée, les numéros des commandes suivantes sont décalés, c'est-à-dire. lorsque vous émettez history -d 2, la 3e entrée devient la 2e, la 4e devient la 3e, etc.

Pour cette raison, lorsque vous utilisez ce code, vous ne pouvez pas appeler manuellement history -d <n>
nest plus petit ou égal au plus grand nombre stocké dansFAILED_COMMANDS
et vous attendre à ce que le code fonctionne correctement.

Il est probablement une bonne idée d'accrocher exit_handlerà EXIT, mais vous pouvez aussi l' appeler à tout moment plus tôt.

GingerPlusPlus
la source