50

我试图防止bash将重复的命令保存到我的历史记录中。这是我所拥有的:

shopt -s histappend
export HISTIGNORE='&:ls:cd ~:cd ..:[bf]g:exit:h:history'
export HISTCONTROL=erasedups
export PROMPT_COMMAND='history -a'

当我登录并.bash_history在内存中时,这可以正常工作。例如:

$ history
    1 vi .bashrc
    2 vi .alias
    3 cd /cygdrive
    4 cd ~jplemme
    5 vi .bashrc
    6 vi .alias

$ vi .bashrc

$ history
    1 vi .alias
    2 cd /cygdrive
    3 cd ~jplemme
    4 vi .alias
    5 vi .bashrc

$ vi .alias

$ history
    1 cd /cygdrive
    2 cd ~jplemme
    3 vi .bashrc
    4 vi .alias

$ exit

但是当我重新登录时,我的历史文件如下所示:

$ history
    1 vi .bashrc
    2 vi .alias
    3 cd /cygdrive
    4 cd ~jplemme
    5 vi .bashrc
    6 vi .alias
    7 vi .bashrc
    8 vi .alias

我究竟做错了什么?

编辑:从中删除shoptPROMPT_COMMAND.bashrc并不能解决问题。

4

5 回答 5

39

据我所知,不可能做你想做的事。我认为这是 bash 历史处理中的一个错误,可以改进。

export HISTCONTROL=ignoreboth:erasedups   # no duplicate entries
shopt -s histappend                       # append history file
export PROMPT_COMMAND="history -a"        # update histfile after every command

这将使内存中的历史记录保持唯一,但是虽然它确实将多个会话的历史记录保存到同一个文件中,但它并没有保持文件本身的历史记录唯一。history -a会将新命令写入文件,除非它与之前的命令相同。它不会像erasedups设置在内存中那样执行完全重复数据删除。

要查看这种愚蠢的行为,请启动一个新的终端会话,检查历史记录,您会看到重复的条目,例如ls. 现在运行ls命令,所有重复的ls将从内存中的历史记录中删除,只留下最后一个。当您运行在历史文件中重复的命令时,内存中的历史会变短,但历史文件本身会继续增长。

我使用自己的脚本按需清理历史文件。

# remove duplicates while preserving input order
function dedup {
   awk '! x[$0]++' $@
}

# removes $HISTIGNORE commands from input
function remove_histignore {
   if [ -n "$HISTIGNORE" ]; then
      # replace : with |, then * with .*
      local IGNORE_PAT=`echo "$HISTIGNORE" | sed s/\:/\|/g | sed s/\*/\.\*/g`
      # negated grep removes matches
      grep -vx "$IGNORE_PAT" $@
   else
      cat $@
   fi
}

# clean up the history file by remove duplicates and commands matching
# $HISTIGNORE entries
function history_cleanup {
   local HISTFILE_SRC=~/.bash_history
   local HISTFILE_DST=/tmp/.$USER.bash_history.clean
   if [ -f $HISTFILE_SRC ]; then
      \cp $HISTFILE_SRC $HISTFILE_SRC.backup
      dedup $HISTFILE_SRC | remove_histignore >| $HISTFILE_DST
      \mv $HISTFILE_DST $HISTFILE_SRC
      chmod go-r $HISTFILE_SRC
      history -c
      history -r
   fi
}

我很想听听更优雅的方法来做到这一点。

注意:如果您通过 HISTTIMEFORMAT 在历史记录中启用时间戳,该脚本将不起作用。

Bash 可以通过以下方式改善这种情况

  1. 修复history -a仅在新数据与内存中的任何历史记录不匹配时才写入新数据,而不仅仅是最后一个。
  2. erasedups如果设置了设置,则在读取文件时去重历史记录。然后,新终端中的简单history -w程序将清理历史文件,而不是上面的愚蠢脚本。
于 2011-09-16T19:22:13.547 回答
7

问题肯定是histappend。在我的系统上测试并确认。

我的相关环境是:

$ set | grep HIST
HISTFILE=/Users/hop/.bash_history
HISTFILESIZE=500
HISTIGNORE=' *:&:?:??'
HISTSIZE=500
$ export HISTCONTROL=erasedups
$ shopt | grep hist
cmdhist         on
histappend      off
histreedit      off
histverify      off
lithist         off

现在想来,问题可能出在history -a. history -w应该写没有任何重复的当前历史记录,所以如果您不介意并发问题,请使用它。

于 2008-12-03T20:34:06.893 回答
5
export HISTCONTROL=ignoreboth
于 2008-12-03T19:17:04.710 回答
3

这是我用的..

[vanuganti@ ~]$ grep HIST .alias*
.alias:HISTCONTROL="erasedups"
.alias:HISTSIZE=20000
.alias:HISTIGNORE=ls:ll:"ls -altr":"ls -alt":la:l:pwd:exit:mc:su:df:clear:ps:h:history:"ls -al"
.alias:export HISTCONTROL HISTSIZE HISTIGNORE
[vanuganti@ ~]$ 

和工作

[vanuganti@ ~]$ pwd
/Users/XXX
[vanuganti@ ~]$ pwd
/Users/XXX
[vanuganti@ ~]$ history | grep pwd | wc -l
       1
于 2011-01-08T01:40:53.257 回答
2

在你的 .bash_profile 添加

alias hist="history -a && hist.py"

然后将其作为 hist.py 放在您的路径上并使其可执行

#!/usr/bin/env python

from __future__ import print_function
import os, sys
home = os.getenv("HOME")
if not home :
    sys.exit(1)
lines = open(os.path.join(home, ".bash_history")).readlines()
history = []
for s in lines[:: -1] :
    s = s.rstrip()
    if s not in history :
        history.append(s)
print('\n'.join(history[:: -1]))

现在当你想要短名单时,只需输入 hist

于 2011-01-08T18:23:09.280 回答