Behalten Sie nur erfolgreiche Befehle im BASH-Verlauf bei

20

Manchmal verstehe ich die Syntax eines Befehls falsch:

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

Ich versuche es noch einmal und mache es richtig:

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

Wie verhindere ich, dass der erste Befehl mit einem anderen Fehlercode als 0 den Verlauf eingibt?

Adam Matan
quelle

Antworten:

20

Ich glaube nicht, dass du das wirklich willst. Mein üblicher Workflow sieht so aus:

  • Geben Sie einen Befehl ein
  • Starte es
  • Beachten Sie, dass es fehlschlägt
  • Drücken Sie die UP-Taste
  • Bearbeiten Sie den Befehl
  • Führen Sie es erneut aus

Wenn der fehlgeschlagene Befehl nicht im Verlauf gespeichert wurde, konnte ich ihn nicht einfach wieder reparieren und erneut ausführen.

Rene Saarsoo
quelle
3
Ich denke, ein besseres Design wäre eine Sitzungsgeschichte und eine permanente Geschichte. Vielen Dank!
Adam Matan
Der Verlauf wird gespeichert, wenn Sie das Terminal verlassen. Während Sie also zu den Befehlen zurückkehren können, die Sie in dieser Terminalsitzung eingegeben haben, werden diese beim Verlassen des Terminals tatsächlich im Bash-Verlauf gespeichert.
zu tun
11

Die einzige Möglichkeit, dies zu tun, wäre die Verwendung history -din $PROMPT_COMMAND. Das Problem bei diesem oder einem anderen Ansatz ist, dass nicht erkennbar ist, ob ein Befehl mit einem Fehler beendet oder mit einem Exit-Code ungleich Null erfolgreich abgeschlossen wurde.

$ grep non_existent_string from_file_that_exists
$ echo $?
1
Bis auf weiteres angehalten.
quelle
4

Es ist gut, den letzten falschen Kommentar zu haben, um ihn zu korrigieren, aber kurz danach wird es möglicherweise zu einem verwirrenden Müll.

Mein Ansatz besteht aus zwei Schritten: Speichern Sie fehlgeschlagene Befehle und entfernen Sie sie später.

Speichern Sie Befehle, bei denen ein Fehler auftritt:

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

trap error_handler ERR

trap command signalswird ausgeführt, commandwenn einer von signals"ausgelöst" wird.

$(command), führt das aus commandund erfasst seine Ausgabe.

Wenn der Befehl fehlschlägt, erfasst dieses Codefragment die Verlaufsnummer des zuletzt im Verlauf gespeicherten Befehls und speichert sie in einer Variablen zum späteren Löschen.

Einfach, funktioniert aber nicht richtig mit HISTCONTROLund HISTIGNORE- Wenn der Befehl aufgrund einer der Variablen nicht im Verlauf gespeichert wird, ist die Verlaufsnummer des zuletzt im Verlauf gespeicherten Befehls die Nummer des vorherigen Befehls. Wenn also ein falscher Befehl nicht im Verlauf gespeichert wird, wird der vorherige Befehl gelöscht.

Etwas kompliziertere Version, die in diesem Fall korrekt funktioniert:

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

Gespeicherte Befehle später entfernen:

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

trap exit_handler EXIT

Erläuterung:

Entfernen Sie beim Verlassen von Bash für jede eindeutige Verlaufsnummer den entsprechenden Verlaufseintrag. Deaktivieren Sie
dann das Kontrollkästchen, FAILED_COMMANDSum keine Befehle zu entfernen, die Verlaufsnummern von bereits gelöschten Befehlen übernommen haben.

Wenn Sie sicher sind, dass FAILED_COMMANDSkeine Duplikate vorhanden sind, können Sie einfach darüber iterieren
(dh schreiben for i in $FAILED_COMMANDS). Wenn Sie jedoch erwarten, dass es nicht vom größten zum kleinsten sortiert wird (in diesem Fall immer), ersetzen Sie es uniqdurch sort -rnu.

Verlaufsnummern in FAILED_COMMANDSmüssen eindeutig sein und vom größten zum kleinsten sortiert werden, da beim Löschen von Einträgen die Nummern der nächsten Befehle verschoben werden - d. H. Wenn Sie ausstellen history -d 2, wird der dritte Eintrag zum zweiten, der vierte zum dritten usw.

Aus diesem Grund können Sie bei Verwendung dieses Codes nicht manuell anrufen, history -d <n>
wo neine kleinere oder gleich große gespeicherte Nummer vorhanden ist,FAILED_COMMANDS
und erwarten, dass der Code ordnungsgemäß funktioniert.

Es ist wahrscheinlich eine gute Idee , Haken exit_handleran EXIT, aber man kann es auch früher jederzeit anrufen.

GingerPlusPlus
quelle