【问题标题】:Prevent duplicates from being saved in bash history [closed]防止将重复项保存在 bash 历史记录中[关闭]
【发布时间】:2010-09-25 04:41:01
【问题描述】:

我试图阻止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

我做错了什么?

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

【问题讨论】:

  • 你解决过这个问题吗?我的 /etc/profile 看起来像这样(在 Mac 上),我有同样的问题: # blog.macromates.com/2008/working-with-history-in-bash # ducea.com/2006/05/15/… export HISTCONTROL=erasedups export HISTSIZE=10000 export HISTTIMEFORMAT="%F-%T%t" shopt - s histappend (试图格式化这半个像样的,不容易)
  • 这是编程中固有的问题,与程序员使用的特定工具有关。我很困惑这个问题会被认为是“离题”(至少有 5 人更多)。多余的 .bash_history 对我来说是一个长期的烦恼,现在已经解决了。不过,这个问题可以移到 Unix stackexchange 站点。

标签: bash command-line cygwin history


【解决方案1】:

在你的 .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

【讨论】:

    【解决方案2】:

    据我所知,不可能做你想做的事。我认为这是 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 会清理历史文件,而不是上面的愚蠢脚本。

    【讨论】:

    • 优秀的答案。如果您希望保留命令的时间顺序(而不是输入顺序),请修改 dedup(),将 awk '! x[$0]++' $@ 替换为 tac $@ | awk '! x[$0]++' | tac
    • @raychi 只是检查一下,当我们接近 2014 年时,这是否仍然是最佳解决方案?
    • 扩展@trusktr 的评论,修复history -a 以仅写入新数据也不起作用,因为它实际上应该删除以前的出现并添加最新的。我怀疑正是由于这种复杂性,他们决定只删除连续的重复项。
    • 如果这是一个独立的脚本,它实际上为我做任何事情的唯一方法是在脚本的最后一行添加对 history_cleanup 函数的调用 :)
    • 我会对 dedup 函数做一点小改动。在 awk de-dup 之前和之后颠倒历史文件的顺序(可能使用“tac”)。这样,最新的重复命令将被保留,而不是最旧的命令,因为 awk 会首先看到它。
    【解决方案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
    

    【讨论】:

    • 谢谢维努。我遇到的问题有点不同。假设历史上有 20 个“ls”。通过键入“ls”,它会删除重复项,因此您的历史记录会更短......至少在会话期间。但是退出并开始一个新的会话,它会保存新的东西并保留旧的副本。撤消擦除的大部分实用程序。 #Facepalm。
    • 附加了与我的 .bash_profile 非常相似的内容:export HISTIGNORE=ls:"ls -la":"cd ..":"cd ~":pwd:exit:su:"sudo -i":clear:ps:"ps$ export HISTSIZE=20000 export HISTCONTROL="erasedups"
    【解决方案4】:
    export HISTCONTROL=ignoreboth
    

    【讨论】:

    • 它的工作,谢谢!但是历史还在记忆不连续的重复命令,有没有办法改进呢?
    • 它的作用是什么?或者更确切地说,它与问题有什么关系?
    【解决方案5】:

    问题肯定是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 应该写入当前历史记录,没有任何重复,所以如果您不介意并发问题,请使用它。

    【讨论】:

      猜你喜欢
      • 2019-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-22
      • 1970-01-01
      • 2011-10-29
      • 2016-08-27
      • 1970-01-01
      相关资源
      最近更新 更多