Сохраняйте в истории BASH только успешные команды

Сохраняйте в истории BASH только успешные команды

Иногда я неправильно понимаю синтаксис команды:

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

Я пробую еще раз и получаю правильно:

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

Как предотвратить попадание в историю первой команды с кодом ошибки, отличным от 0?

решение1

Я не думаю, что вы действительно этого хотите. Мой обычный рабочий процесс выглядит так:

  • Введите команду
  • Запустить его
  • Обратите внимание, что это не удается.
  • Нажмите клавишу ВВЕРХ
  • Изменить команду
  • Запустите его снова

Теперь, если бы неудавшаяся команда не была сохранена в истории, я бы не смог легко ее исправить и запустить снова.

решение2

Единственный способ, который я могу придумать, — использовать history -din $PROMPT_COMMAND. Проблема с этим или любым другим подходом заключается в том, что невозможно определить, завершилась ли команда с ошибкой или была успешно завершена с ненулевым кодом выхода.

$ grep non_existent_string from_file_that_exists
$ echo $?
1

решение3

Хорошо иметь последний неверный комментарий, чтобы его исправить, но вскоре после этого он превращается в потенциально запутанный мусор.

Мой подход состоит из двух этапов: сохранять команды, которые не выполняются, когда они происходят, и удалять их позже.

Сохраните команды, которые не выполняются, если они:

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

trap error_handler ERR

trap command signalsвыполняется command, когда один из них signals«поднят».

$(command), выполняет commandи фиксирует его вывод.

При сбое команды этот фрагмент кода сохраняет номер историипоследняя команда сохранена в историии сохраняет его в переменной для будущего удаления.

Просто, но работает некорректно с HISTCONTROLи HISTIGNORE– когда команда не сохраняется в истории из-за одной из переменных,номер истории последней команды, сохраненной в историиявляется командой предыдущей команды; поэтому, если неверная команда не сохранена в истории, предыдущая команда будет удалена.

Немного более сложная версия, которая в данном случае работает правильно:

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

Удалите сохраненные команды позже:

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

trap exit_handler EXIT

Объяснение:

При выходе из Bash для каждого уникального номера истории удалите соответствующую запись истории,
затем очистите, FAILED_COMMANDSчтобы не удалять команды, унаследовавшие номера истории от уже удаленных команд.

Если вы уверены, что он FAILED_COMMANDSбудет свободен от дубликатов, вы можете просто перебрать его
(т. е. написать for i in $FAILED_COMMANDS). Если же вы ожидаете, что он не будет отсортирован от наибольшего к наименьшему (в этом случае это всегда так), замените uniqна sort -rnu.

Номера истории FAILED_COMMANDSдолжны быть уникальными и отсортированы от большего к меньшему, поскольку при удалении записи номера следующих команд сдвигаются, т. е. при выполнении history -d 23-я запись становится 2-й, 4-я становится 3-й и т. д.

Из-за этого при использовании этого кода вы не можете вручную вызвать функцию history -d <n>
where, nкоторая меньше или равна наибольшему числу, хранящемуся в,FAILED_COMMANDS
и ожидать, что код будет работать правильно.

Вероятно, хорошей идеей будет сделать звонок exit_handlerв EXIT, но вы также можете позвонить в любое время раньше.

Связанный контент