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

its*_*_me 99 bash command-history

首先,这不是 SE 上任何现有线程的重复。我已经阅读了关于更好的 bash 历史的这两个线程(第 1 个第 2 个),但没有一个答案有效 - 顺便说一下,我在 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
Run Code Online (Sandbox Code Playgroud)

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

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

roz*_*acz 139

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

1. 什么有效

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

这有两个缺点:

  1. 在登录(打开终端)时,历史文件中的最后一条命令被读入当前终端的历史缓冲区两次;
  2. 不同终端的缓冲区与历史文件不保持同步。

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

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

[编辑] “而获胜者是……”

这就是它得到的。使两者和共同历史记录同时工作是唯一的选择erasedups可能是您所有问题的最终解决方案,Aahan。


2. 为什么选项 2似乎不起作用(或者:什么确实没有按预期工作)?

正如我所提到的,上述每种解决方案的工作方式都不同。但对设置如何工作的最误导性解释来自分析historycommand的输出。在许多情况下,该命令可能会给出错误的输出。为什么?因为它在! 中包含的其他命令序列之前执行。historyPROMPT_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需要从文件恢复历史缓冲器,从而最终使整个终端会话共享历史。

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

  • 您的解决方案 3 实际上不起作用。它非常有问题,并且取决于用户在不同终端中输入的顺序。这通常会导致命令丢失,尤其是在终端之间来回切换时。来源:试了一下。 (7认同)
  • 在我的情况下,我不想立即共享历史记录,只是在关闭时收敛,同时避免历史记录丢失(例如在没有干净退出的情况下终止)。我决定将`history -a`添加到内存安全提示中,并将选项3的序列放入陷阱函数(`trap deduphistory EXIT`)。 (6认同)
  • *`history -c` 也是必需的,因为它可以防止在每个命令之后破坏历史缓冲区* 为什么会发生这种破坏? (4认同)
  • +1 表示“但对设置如何工作的最误导性解释来自分析历史命令的输出。” 我认为这是 OP 问题的核心。优秀的演绎。 (3认同)
  • 选项 3 刚刚清除了我所有的 100,000 行历史记录 :( (bash 3.2.25(1)-release) (3认同)
  • 我终于看到你的观点了。你所说的“重复”指的不是我自己。我只关注*相同命令的序列*。更新了我的答案 - 请参阅“选项 3”。此外,对于您的情况,要测试历史记录如何工作,您实际上应该使用`watch "tail -n 20 .bash_history"` 而不是 `tail -f .bash_history`。 (2认同)
  • 上面关于使用“tgrap dedupe_history EXIT”的评论与我选择的解决方案相同。与在每次提示时读取/写入我的 10,000 行历史文件相比,似乎不那么浪费,也更容易受到竞争的影响。多年来一直为我工作良好。我自己的重复数据删除函数读取为 `tac < ~/.bash_history | awk '!a[$0]++' | tac >/tmp/deduped && mv -f /tmp/deduped ~/.bash_history` (否则这个答案很精彩,并且增强了我的理解!) (2认同)
  • @Piotr Dobrogost 当您输入命令时,它会附加到历史记录列表中,并且该列表将写入文件。输入不同的命令,添加到文件中的任何新行都将使用“history -n”附加到历史列表中,在本例中是我们输入的第一个命令。所以你会在历史列表中复制第一个命令,因为 `history -n` 不会删除重复项。`历史-c; history -r` 将列表与文件同步,使文件中的所有内容“读取”,并防止“history -n”将其放入列表中。 (2认同)

jas*_*yan 8

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

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

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

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

默认大小也在手册中:

HISTSIZE 命令历史记录中要记住的命令数(参见下面的 HISTORY)。默认值为 500。

保存多行命令:

cmdhist shell选项,如果已启用,导致shell尝试保存多行命令的每一行中相同的历史条目,将分号在必要时维护语法的正确性。该lithist外壳选项使得shell保存嵌入的新行而不是分号的命令。

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


小智 8

这就是我想出来的,到目前为止我很满意……

alias hfix='history -n && history | sort -k2 -k1nr | uniq -f1 | sort -n | cut -c8- > ~/.tmp$$ && history -c && history -r ~/.tmp$$ && history -w && rm ~/.tmp$$'  
HISTCONTROL=ignorespace  
shopt -s histappend  
shopt -s extglob  
HISTSIZE=1000  
HISTFILESIZE=2000  
export HISTIGNORE="!(+(*\ *))"  
PROMPT_COMMAND="hfix; $PROMPT_COMMAND" 
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 是的,这很复杂……但是,它会删除所有重复项,但仍保留每个终端内的年表!
  • 我的HISTIGNORE忽略所有没有参数的命令。某些人可能不希望这样做,可以将其排除在外。


Hon*_*ule 5

将@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"
Run Code Online (Sandbox Code Playgroud)
  • historymerge函数从历史文件中加载会话外行,将它们与会话历史结合起来,用重复数据删除写出一个新的历史文件(从而压缩任何先前附加的重复行),并重新加载历史。

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

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

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

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