BASH 履歴に成功したコマンドのみ保存する

BASH 履歴に成功したコマンドのみ保存する

時々、コマンドの構文を誤解することがあります。

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

もう一度試して正解です:

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

エラー コードが 0 以外の最初のコマンドが履歴に入らないようにするにはどうすればよいですか?

答え1

あまりそんなことは望んでいないと思います。私の通常のワークフローは次のようになります。

  • コマンドを入力
  • それを実行します
  • 失敗に気づく
  • 上キーを押す
  • コマンドを編集する
  • もう一度実行してください

さて、失敗したコマンドが履歴に保存されていなかったら、簡単に元に戻して修正し、再度実行することができませんでした。

答え2

これを実行するために私が考えられる唯一の方法は、 を使用することですhistory -d$PROMPT_COMMANDこの方法や他の方法の問題点は、コマンドがエラーで終了したのか、ゼロ以外の終了コードで正常に完了したのかを判断できないことです。

$ grep non_existent_string from_file_that_exists
$ echo $?
1

答え3

最後に間違ったコメントを残して訂正するのは良いことですが、その後すぐに、混乱を招く可能性のあるゴミになってしまう可能性があります。

私のアプローチは 2 段階です。失敗したコマンドは失敗したときに保存し、後で削除します。

失敗したコマンドを次の場合に保存します。

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

trap error_handler ERR

trap command signalscommandいずれかが「発生した」ときに実行されますsignals

$(command)を実行し、commandその出力をキャプチャします。

コマンドが失敗すると、このコードスニペットは履歴番号をキャプチャします。履歴に保存された最後のコマンド、将来の削除に備えて変数に保存します。

シンプルですが、変数の1つが原因でコマンドが履歴に保存されない場合、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 2、3 番目のエントリが 2 番目になり、4 番目のエントリが 3 番目になります。

そのため、このコードを使用する場合、に格納されている最大値より小さいか等しいhistory -d <n>
nFAILED_COMMANDS
を手動で呼び出して 、コードが適切に動作することを期待することはできません。

exit_handlerでフックするのがおそらく良い考えですEXITが、それより前のいつでも呼び出すこともできます。

関連情報