如何在注销时从多行 Bash 历史记录中过滤某些命令?

如何在注销时从多行 Bash 历史记录中过滤某些命令?

我已将 Bash 配置为保存带有嵌入换行符的多行历史记录条目。

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

当我退出 shell 时,我的历史记录会附加到~/.bash_history分隔条目的时间戳,例如:

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

我尝试设置HISTIGNORE过滤掉一些命令,

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

但很快意识到我更喜欢将所有内容包含在当前会话的(内存中)历史记录中,我只想过滤写入磁盘以供将来会话的内容。

我想利用杠杆~/.bash_logout这个过滤功能(我一直在研究这些 答案几个相关的问题)。

多行条目使过滤变得有点复杂。我需要识别时间戳后面的第一行与其中一个可忽略模式匹配的条目,并排除整个条目,包括时间戳。

我想出了一种用 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

即使第一行与“忽略”模式匹配也保留多行条目的要求增加了一些复杂性。我最终在 Awk 中编写了一个有限状态机来在 HISTFILE 写入磁盘后对其进行过滤(在写入之前我找不到触发过滤的方法)。

〜/ .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
}

我将 shellHistBackup 包含在“trap shellExit EXIT”中

我希望这对某人有用!

相关内容