Zachowaj tylko udane polecenia w historii BASH

20

Czasami źle rozumiem składnię polecenia:

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

Próbuję ponownie i mam rację:

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

Jak zapobiec pierwszej komendzie z kodem błędu innym niż 0, aby przejść do historii?

Adam Matan
źródło

Odpowiedzi:

20

Nie sądzę, żebyś tego naprawdę chciał. Mój zwykły przepływ pracy wygląda następująco:

  • Wpisz polecenie
  • Uruchom
  • Zauważ, że się nie udaje
  • Naciśnij klawisz UP
  • Edytuj polecenie
  • Uruchom ponownie

Teraz, jeśli nieudane polecenie nie zostało zapisane w historii, nie mogłem go łatwo przywrócić, aby naprawić i uruchomić ponownie.

Rene Saarsoo
źródło
3
Wydaje mi się, że lepszym projektem byłaby historia sesji i historia stała. Dzięki!
Adam Matan
Historia zostaje zapisana po wyjściu z terminala. Więc chociaż możesz wrócić do poleceń wpisanych w tej sesji terminala, tak naprawdę zostaje on zapisany w historii bash po wyjściu z terminala.
Zrobić
11

Jedynym sposobem mogę myśleć to zrobić byłoby użyć history -dw $PROMPT_COMMAND. Problem z tym lub innym podejściem polega na tym, że nie można stwierdzić, czy polecenie zakończyło się błędem, czy zakończyło się pomyślnie z niezerowym kodem wyjścia.

$ grep non_existent_string from_file_that_exists
$ echo $?
1
Wstrzymano do odwołania.
źródło
4

Dobrze jest mieć ostatni niepoprawny komentarz, aby go poprawić, ale wkrótce potem staje się potencjalnie mylącym śmieciem.

Moje podejście jest dwuetapowe: przechowuj polecenia, które się nie powiedzie, i usuwaj je później.

Przechowuj polecenia, które nie działają, gdy:

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

trap error_handler ERR

trap command signalswykonuje się, commandgdy jeden z nich signalsjest „podniesiony”.

$(command), wykonuje commandi przechwytuje dane wyjściowe.

Gdy polecenie nie powiedzie się, ten fragment kodu przechwytuje w historii numer ostatniego polecenia zapisanego w historii i zapisuje go w zmiennej do przyszłego usunięcia.

Prosty, ale działa niepoprawnie z HISTCONTROLi HISTIGNORE- gdy polecenie nie jest zapisane w historii z powodu jednej ze zmiennych, numer historii ostatniego polecenia zapisanego w historii jest numerem poprzedniego polecenia; więc jeśli nieprawidłowe polecenie nie zostanie zapisane w historii, poprzednie polecenie zostanie usunięte.

Nieco bardziej skomplikowana wersja, która w tym przypadku działa poprawnie:

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

Usuń przechowywane polecenia później:

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

trap exit_handler EXIT

Wyjaśnienie:

Wychodząc z Bash, dla każdego unikalnego numeru historii usuń odpowiedni wpis historii,
a następnie usuń zaznaczenie, FAILED_COMMANDSaby nie usuwać poleceń, które odziedziczyły numery historii z już usuniętych poleceń.

Jeśli masz pewność, że FAILED_COMMANDSbędą wolne od duplikatów, możesz po prostu iterować
(np. Pisać for i in $FAILED_COMMANDS). Jeśli jednak można oczekiwać, że nie mogą być sortowane od największego do najmniejszego (w tym przypadku to zawsze jest), wymienić uniqz sort -rnu.

Numery historii FAILED_COMMANDSmuszą być unikalne i posortowane od największych do najmniejszych, ponieważ po usunięciu wpisu numery kolejnych poleceń są przesuwane - tzn. kiedy wydajesz history -d 2, 3. wpis staje się 2., 4. staje się 3. itd.

Z tego powodu podczas korzystania z tego kodu nie można ręcznie wywoływać, history -d <n>
gdzie njest mniejszy lub równy największemu przechowywanemu numerowiFAILED_COMMANDS
i oczekiwać, że kod będzie działał poprawnie.

Prawdopodobnie dobrym pomysłem jest zaczepienie exit_handlersię EXIT, ale możesz też zadzwonić w dowolnym momencie wcześniej.

GingerPlusPlus
źródło