ログアウト時に複数行のBash履歴から特定のコマンドをフィルタリングする方法は?

ログアウト時に複数行のBash履歴から特定のコマンドをフィルタリングする方法は?

改行を含む複数行の履歴エントリを保存するようにBashを設定しました。

shopt -s cmdhist lithist
export HISTTIMEFORMAT='%F %T '

シェルを終了すると、履歴に~/.bash_history項目を区切るタイムスタンプが追加されます。たとえば、次のようになります。

#1501293767
foo() {
echo foo
}
#1501293785
ls

HISTIGNOREいくつかのコマンドをフィルタリングするように設定してみました。

HISTIGNORE='ls[ ]*:man[ ]*:cat[ ]*'

しかしすぐに、私は現在のセッションの(メモリ内)レコードにすべてを含めることを好むことに気づきました。

~/.bash_logoutこのフィルタ機能を活用したいと思います。これら 回答いくつかの関連質問)。

複数行エントリを使用すると、フィルタリングがより複雑になります。タイムスタンプの後の最初の行が無視できるパターンの1つと一致するものを識別し、タイムスタンプを含む項目全体を除外する必要があります。

私はawkを使ってこれを行う方法を見つけましたが、awkのスキルがそれほど良くないので、より良い方法があるかどうか疑問に思います。

filter_history() {
  tmpfile=$(mktemp)
  history -w $tmpfile

  awk '/^#[[:digit:]]{10}$/  { timestamp=$0; ignore=0; next }
       length(timestamp) > 0 && /^(ls|man|cat)([^[:alnum:]]|$)/ { timestamp=""; ignore=1 ; next }
       length(timestamp) > 0 { print timestamp; timestamp=""; print; next }
       ignore == 0           { print }' \
      $tmpfile >> $HISTFILE && rm $tmpfile
}

編集する:実際には、複数行の項目をフィルタリングしたい状況は思い出されません。例えば、 で始まってもls複数行にまたがっている場合は、おそらく興味深く覚えておく価値があることをしているでしょう。

答え1

最初の行が「無視」パターンと一致していても、複数行の項目を維持する必要があるという要件により、少し複雑になります。私はHISTFILEがディスクに書き込まれた後、それをフィルタリングするためにAwkに有限ステートマシンを作成しました(書き込まれる前にフィルタリングをトリガーする方法が見つかりませんでした)。

~/.bashrc:

# if shell is interactive, filter history upon exit
if [[ $- == *i* ]]; then
  trap '$HOME/.bash_history_filter >/dev/null 2>&1 &' EXIT
fi

~/.bash_history_filter:

tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT

filter_script="$HOME/.bash_history_filter.awk"
persisted_history="${HISTFILE:-$HOME/.bash_history}"

if [[ -r "$filter_script" && -r "$persisted_history" ]]; then
  awk -f "$filter_script" "$persisted_history" > "$tmpfile"

  mv "$tmpfile" "$persisted_history"
fi

有限状態マシン

~/.bash_history_filter.awk:

/^#[[:digit:]]{10}$/ {
  timestamp = $0
  histentry = ""
  next
}
$1 ~ /^(ls?|man|cat)$/ {
  if (! timestamp) {
    print
  } else {
    histentry = $0
  }
  next
}
timestamp {
  print timestamp
  timestamp = ""
}
histentry {
  print histentry
  histentry = ""
}
{ print }

いくつかの関連投稿(ここそしてここ)これはsedを使用して行うことができると思われます。まだ調べていませんが、比較してどうか知りたいです。

答え2

完全性のためにbashレコードを処理する方法は次のとおりです! (フィルタとバックアップ)

フィルタリングの有無にかかわらず、HISTFILEは自分の履歴を気にする人には十分ではなく、大きなHISTFILEを使用することもお勧めできません。

function shellHist () #find old hist
{ 
    if [[ -f ${HISTFILE}.${1} ]]; then
        cat ${HISTFILE}.${1};
    else
        grep -h "${@:-.}" ${HISTFILE}.*;
    fi
}
function shellHistBackup () 
{ 
    [[ -n ${HISTFILE} ]] || return;
    # pidKill0 && return; # do nothing if the job is actually running elsewhere
    {
    while read histLine; do
        if ! grep -q "^${histLine}$" ${HISTFILE}.${histLine%% *} 2> /dev/null; then
            echo "${histLine}" >> ${HISTFILE}.${histLine%% *};
        fi;
    done < ${HISTFILE}
    for fltr in ls cat any ; do
        rm ${HISTFILE}.${fltr}
    done
    } & # pidLog # related to pidKill0
}

"trap shellExit EXIT"にshellHistBackupを含めます。

これが誰かに役立つことを願っています!

関連情報