防止在bash历史记录中保存重复项

时间:2022-08-27 04:18:50

I'm trying to prevent bash from saving duplicate commands to my history. Here's what I've got:

我正在尝试阻止bash将重复命令保存到我的历史记录中。这是我得到的:

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

This works fine while I'm logged in and .bash_history is in memory. For example:

当我登录并且.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

But when I log back in, my history file looks like this:

但是当我重新登录时,我的历史文件看起来像这样:

$ 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

What am I doing wrong?

我究竟做错了什么?

EDIT: Removing the shopt and PROMPT_COMMAND lines from .bashrc does not fix the problem.

编辑:从.bashrc中删除shopt和PROMPT_COMMAND行不能解决问题。

5 个解决方案

#1


33  

As far as I know, it is not possible to do what you want. I see this as a bug in bash's history processing that could be improved.

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

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

This will keep the in memory history unique, but while it does saves history from multiple sessions into the same file, it doesn't keep the history in the file itself unique. history -a will write the new command to the file unless it's the same as the one immediately before it. It will not do a full de-duplication like the erasedups setting does in memory.

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

To see this silliness in action, start a new terminal session, examine the history, and you'll see repeated entries, say ls. Now run the ls command, and all the duplicated ls will be removed from the history in memory, leaving only the last one. The in memory history becomes shorter as you run commands that are duplicated in the history file, yet the history file itself continues to grow.

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

I use my own script to clean up the history file on demand.

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

# 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
}

I'd love to hear more elegant ways to do this.

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

Note: the script won't work if you enable timestamp in history via HISTTIMEFORMAT.

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

Bash can improve the situation by

Bash可以改善这种状况

  1. fix history -a to only write new data if it does not match any history in memory, not just the last one.
  2. 修复历史记录-a如果它与内存中的任何历史记录都不匹配,则只写入新数据,而不仅仅是最后一个。

  3. de-deduplicate history when files are read if erasedups setting is set . A simple history -w in a new terminal would then clean up the history file instead of the silly script above.
  4. 如果设置了erasedups设置,则在读取文件时去除重复数据删除历史记录。然后,新终端中的简单历史记录将清除历史文件而不是上面的愚蠢脚本。

#2


7  

The problem is definitely the histappend. Tested and confirmed on my system.

问题绝对是他的意见。在我的系统上测试并确认。

My relevant environment is:

我的相关环境是:

$ 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

Now that I think about it, the problem is probably with the history -a. history -w should write the current history without any duplicates, so use that if you don't mind the concurrency issues.

现在我考虑一下,问题可能在于历史--a。历史记录-w应该在没有任何重复的情况下编写当前历史记录,因此如果您不介意并发问题,请使用它。

#3


5  

export HISTCONTROL=ignoreboth

#4


2  

Here is what I use..

这是我用的..

[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@ ~]$ 

and working

[vanuganti@ ~]$ pwd
/Users/XXX
[vanuganti@ ~]$ pwd
/Users/XXX
[vanuganti@ ~]$ history | grep pwd | wc -l
       1

#5


1  

inside your .bash_profile add

在你的.bash_profile内添加

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

then put this on your path as hist.py and make it executable

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

#!/usr/bin/env python
f = open('/Users/joe/.bash_history')
l = f.readlines()
l.reverse()
short = []
for s in l:
    if s.rstrip() not in short:
        short.append(s.rstrip())
short.reverse()
for s in short:
    print s

now when you want the short list just type hist

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

#1


33  

As far as I know, it is not possible to do what you want. I see this as a bug in bash's history processing that could be improved.

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

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

This will keep the in memory history unique, but while it does saves history from multiple sessions into the same file, it doesn't keep the history in the file itself unique. history -a will write the new command to the file unless it's the same as the one immediately before it. It will not do a full de-duplication like the erasedups setting does in memory.

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

To see this silliness in action, start a new terminal session, examine the history, and you'll see repeated entries, say ls. Now run the ls command, and all the duplicated ls will be removed from the history in memory, leaving only the last one. The in memory history becomes shorter as you run commands that are duplicated in the history file, yet the history file itself continues to grow.

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

I use my own script to clean up the history file on demand.

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

# 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
}

I'd love to hear more elegant ways to do this.

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

Note: the script won't work if you enable timestamp in history via HISTTIMEFORMAT.

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

Bash can improve the situation by

Bash可以改善这种状况

  1. fix history -a to only write new data if it does not match any history in memory, not just the last one.
  2. 修复历史记录-a如果它与内存中的任何历史记录都不匹配,则只写入新数据,而不仅仅是最后一个。

  3. de-deduplicate history when files are read if erasedups setting is set . A simple history -w in a new terminal would then clean up the history file instead of the silly script above.
  4. 如果设置了erasedups设置,则在读取文件时去除重复数据删除历史记录。然后,新终端中的简单历史记录将清除历史文件而不是上面的愚蠢脚本。

#2


7  

The problem is definitely the histappend. Tested and confirmed on my system.

问题绝对是他的意见。在我的系统上测试并确认。

My relevant environment is:

我的相关环境是:

$ 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

Now that I think about it, the problem is probably with the history -a. history -w should write the current history without any duplicates, so use that if you don't mind the concurrency issues.

现在我考虑一下,问题可能在于历史--a。历史记录-w应该在没有任何重复的情况下编写当前历史记录,因此如果您不介意并发问题,请使用它。

#3


5  

export HISTCONTROL=ignoreboth

#4


2  

Here is what I use..

这是我用的..

[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@ ~]$ 

and working

[vanuganti@ ~]$ pwd
/Users/XXX
[vanuganti@ ~]$ pwd
/Users/XXX
[vanuganti@ ~]$ history | grep pwd | wc -l
       1

#5


1  

inside your .bash_profile add

在你的.bash_profile内添加

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

then put this on your path as hist.py and make it executable

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

#!/usr/bin/env python
f = open('/Users/joe/.bash_history')
l = f.readlines()
l.reverse()
short = []
for s in l:
    if s.rstrip() not in short:
        short.append(s.rstrip())
short.reverse()
for s in short:
    print s

now when you want the short list just type hist

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