Bash 历史记录:“ignoredups”和“erasedups”设置与跨会话的共同历史记录冲突

Bash 历史记录:“ignoredups”和“erasedups”设置与跨会话的共同历史记录冲突

首先,这不是 SE 上任何现有线程的重复。我已经读过这两篇文章(第一名,第二名)关于更好的 bash 历史,但没有一个答案有效 - - 顺便说一句,我使用的是 Fedora 15。

我将以下内容添加到.bashrc用户目录(/home/aahan/)中的文件中,但它不起作用。有人有线索吗?

HISTCONTROL=ignoredups:erasedups  # no duplicate entries
HISTSIZE=1000                     # custom history size
HISTFILESIZE=100000                 # custom history file size
shopt -s histappend                      # append to history, don't overwrite it
PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"  # Save and reload the history after each command finishes

好吧,这就是我想要的 bash 历史记录(优先级):

  • 不存储重复项,删除任何现有的
  • 立即与所有打开的终端共享历史记录
  • 始终附加历史记录,而不是覆盖它
  • 将多行命令存储为单个命令(默认情况下关闭)
  • 默认历史记录大小和历史文件大小是多少?

答案1

这实际上是一个非常有趣的行为,我承认我一开始就大大低估了这个问题。但首先是事实:

1. 什么有效

该功能可以通过多种方式实现,尽管每种方式的工作方式略有不同。请注意,在每种情况下,要将历史记录“转移”到另一个终端(更新),必须Enter在他/她想要检索历史记录的终端中按下。

  • 选项1:

     shopt -s histappend
     HISTCONTROL=ignoredups
     PROMPT_COMMAND="history -a; history -n; $PROMPT_COMMAND"
    

这有两个缺点:

  1. 登录(打开终端)时,历史文件中的最后一个命令会被两次读取到当前终端的历史缓冲区中;
  2. 不同终端的缓冲区与历史文件不保持同步。
  • 选项2:

     HISTCONTROL=ignoredups
     PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"
    

(是的,不需要shopt -s histappend,是的,它必须 history -c中间PROMPT_COMMAND)这个版本还有两个重要的缺点:

  1. 历史文件必须被初始化。它必须包含至少一个非空行(可以是任何东西)。
  2. history命令可能会给出错误的输出 - 见下文。

[编辑] “最终获胜者是...”

  • 选项3:

     HISTCONTROL=ignoredups:erasedups
     shopt -s histappend
     PROMPT_COMMAND="history -n; history -w; history -c; history -r; $PROMPT_COMMAND"
    

就目前而言。它是仅有的可以选择让erasedups共同历史同时工作。 可能是最终解决方案解决你所有的问题,阿汉。


2. 为什么会这样选项2似乎不起作用(或者:到底是什么没有按预期工作)?

正如我所提到的,上述每个解决方案的工作原理都不同。但对设置如何工作的最误导性解释来自分析history命令的输出。在许多情况下,该命令可以给出错误的输出。为什么?因为它被执行了!history中包含的其他命令的序列PROMPT_COMMAND然而,当使用第二个或第三个选项时,可以监视.bash_history内容的变化(watch -n1 "tail -n20 .bash_history"例如使用)并查看真实的历史记录是什么。

3. 为什么选项3有这么复杂吗?

这一切都取决于erasedups工作方式。正如 bash 手册所述, “(...)erasedups导致在保存该行之前从历史列表中删除与当前行匹配的所有先前行”。所以这确实是OP想要的(不仅仅是像我之前想的那样,没有重复出现按顺序。这就是为什么每个history -.命令必须或不能在 中的原因PROMPT_COMMAND

  • history -n history -w在读取.bash_history从任何其他终端保存的命令之前,

  • history -w 在 bash 检查命令是否重复后,将新的历史记录保存到文件中,

  • history -a 一定不放置在那里而不是history -w,因为它将向文件添加任何新命令,无论它是否被检查为重复。

  • history -c也是需要的因为它可以防止在每个命令之后破坏历史缓冲区,

  • 最后history -r需要的从文件恢复历史缓冲区,从而最终使历史记录在终端会话之间共享。

请注意,此解决方案会将来自其他终端的所有历史记录放在当前终端中输入的最新命令的前面,从而弄乱历史记录顺序。它也不会删除历史文件中已有的重复行,除非您再次输入该命令。

答案2

在提示命令中,您正在使用-c开关。从man bash

-C   通过删除所有条目来清除历史列表

要与所有打开的终端共享您的历史记录,您可以使用-n

-n   将历史文件中尚未读取的历史行读取到当前历史列表中。这些是自当前 bash 会话开始以来附加到历史文件的行。

默认尺寸也在手册中:

HISTSIZE 命令历史记录中要记住的命令数量(请参阅下面的“历史记录”)。默认值为 500。

保存多行命令:

CMDHISTshell 选项(如果启用)会导致 shell 尝试将多行命令的每一行保存在同一历史记录条目中,并在必要时添加分号以保持语法正确性。这石质主义者shell 选项使 shell 使用嵌入的换行符而不是分号来保存命令。

另外,您不应该在 HIST* 命令前面加上export— 它们是仅 bash 变量而不是环境变量:HISTCONTROL=ignoredups:erasedups就足够了。

答案3

将 @rozcietrzewiacz 的选项 3 与退出陷阱结合使用将使终端能够维护自己的独立历史会话,这些会话在关闭时会收敛。它甚至似乎可以很好地处理共享远程主目录的不同计算机上的多个会话。

export HISTSIZE=5000
export HISTFILESIZE=5000
export HISTCONTROL=ignorespace:erasedups
shopt -s histappend
function historymerge {
    history -n; history -w; history -c; history -r;
}
trap historymerge EXIT
PROMPT_COMMAND="history -a; $PROMPT_COMMAND"
  • historymerge函数从历史文件中加载会话外行,将它们与会话历史记录结合起来,通过重复数据删除写出一个新的历史文件(从而压缩任何先前附加的重复行),然后重新加载历史记录。

  • 保留history -a提示可以最大程度地减少丢失历史记录的可能性,因为它会更新历史记录文件,而无需对每个命令进行重复数据删除。

  • 最后,陷阱historymerge在会话关闭时触发,以获取最新的干净历史记录文件,其中最近关闭的会话的命令冒泡到文件末尾(我认为)。

这样,每个终端会话从启动开始就有自己独立的历史记录。我发现这更有用,因为我倾向于为不同的任务打开不同的终端(因此我想在每个终端中重复不同的命令)。幸运的是,它比理解多个终端不断尝试重新同步它们的方式要简单得多。已订购以分布式方式记录历史(尽管您仍然可以故意使用该historymerge功能来选择会话或所有会话)。

请注意,您需要大小限制足够大以容纳所需的历史记录长度所有并发活动会话可能添加的非重复数据删除行的数量。 5000 对我来说已经足够了,很大程度上要归功于HISTIGNORE过滤掉垃圾低价值命令的广泛使用。

答案4

在您的.bashrc第一个添加中:

HISTCONTROL='erasedups'

然后添加以下历史记录清理辅助函数(它会删除重复项):

clean_bash_history_file() {
  bash_history_file=$(mktemp "$USER"_bash_historyXXXXXX)
  awk 'NR == FNR { a[$0]++; next; }; ++b[$0] == a[$0]' \
      "$HOME/.bash_history" "$HOME/.bash_history" > "$bash_history_file"
  mv "$bash_history_file" "$HOME/.bash_history"
  unset bash_history_file
}

然后制作如下历史更新功能:

update_history() {   
    history -a
    clean_bash_history_file
    history -c
    history -r
}

最后添加

PROMPT_COMMAND=update_history

这将保持正确的历史顺序,并且保证删除所有重复项。

它还允许HISTFILESIZE大于HISTSIZE。只需确保shopt -s histappend也在您的.bashrc

相关内容