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


84

首先,这不是SE上任何现有线程的重复。我已经阅读了有关更好的bash历史的这两个线程(1st2nd),但是没有一个答案有效--顺便说一下,我在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历史记录(优先级):

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

撞!任何人?没用。Fedora 15(使用Gnome 3并在Windows主机虚拟机上运行)是否可能有问题?
its_me 2011年

请不要在此处“颠簸”帖子。如果他们没有得到答复,则需要更清楚地询问,包括正确的上下文线索或其他内容。如果您需要关注自己的帖子,则可以发布赏金,以帮助吸引更多人关注他们。
Caleb

1
您确定甚至在使用bash吗?(echo $SHELL)。如果您从打开的Shell手动运行设置,这些设置是否起作用?显然,由于它们确实适用于许多其他设备,因此设置是正确的,因此您只是在错误地实施它们。而且,没有Fedora15 / Gnome3 /成为虚拟机与的实际功能无关bash
Caleb

@Caleb,首先抱歉增加职位。另外,我也尽力弄清楚了。不,我没有在外壳中发布它们。我只是将它们复制粘贴到.bashrc文件中。错了吗 您可以使用实际的shell命令在此帖子中添加“答案”吗?(请
多多包涵

1
您在脚本或.bashrcARE实际外壳命令中编写的所有内容。脚本只是一系列的Shell命令。您最近所做的删除export位的编辑也是一个坏主意,应该保留。
Caleb

Answers:


119

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

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命令的输出。在许多情况下,该命令会给出错误的输出。为什么?因为它在!中包含的其他命令序列之前执行。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 是有,以保存到文件并删除重复的历史,

  • history -a 一定不要放在那里history -w,因为它不会触发删除重复项,

  • history -c要这样做,因为它可以防止在每个命令之后破坏历史记录缓冲区,

  • 最后,history -r需要从文件恢复历史缓冲器,从而最终使整个终端会话共享历史。


2
+1表示“但是,关于设置如何工作的最令人误解的解释来自分析历史命令的输出。” 我认为这是《任择议定书》问题的核心。优秀的演绎。
jasonwryan 2011年

2
我终于明白了你的意思。“重复”是指我自己以外的其他意思。我只关注同一命令的序列。更新了我的答案-请参阅“选项3”。另外,对于您的情况,要测试历史记录的工作方式,您应该实际使用watch "tail -n 20 .bash_history"而不是tail -f .bash_history
rozcietrzewiacz 2011年

2
选项3抹去了我所有100,000行的历史:((bash 3.2.25(1)-release)
Felipe Alvarez

2
history -c还需要使用它,因为它可以防止在每个命令之后都丢弃历史记录缓冲区
Piotr Dobrogost '16

2
您的解决方案3实际上不起作用。这非常容易出错,并且取决于用户在不同终端中输入的顺序。这通常会导致命令丢失,尤其是在终端之间来回切换时。资料来源:尝试过。
6cef

8

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

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

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

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

默认大小也在手册中:

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

要保存多行命令:

如果启用了cmdhist shell选项,则该命令将使Shell尝试将多行命令的每一行保存在同一历史记录条目中,并在必要时添加分号以保持语法正确性。该lithist外壳选项使得shell保存嵌入的新行而不是分号的命令。

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


那么,这export PROMPT_COMMAND="history -a; history -n; history -r; $PROMPT_COMMAND"是正确的吗?另外,您知道我如何使历史记录文件将多行命令存储为单个命令(默认情况下处于关闭状态)吗?
its_me 2011年

1
是。按照上面引用的手册页,shopt -s cmdhist将保存多行。
jasonwryan 2011年

好吧,我尽力了。看起来HISTCONTROL=ignoredups:erasedups不起作用。我还尝试过在.bashrc文件中添加自定义功能(包括自定义功能)。知道有什么问题吗?我在虚拟机上使用Fedora 15-Windows 7主机。
its_me 2011年

确保其他启动文件(例如/etc/bashrc.bash_profile中没有覆盖它的文件。
jasonwryan 2011年

我看不到.bash_profile文件的任何问题。至于内容中/etc/bashrc我把它放在这里,请大家看看- pastebin.com/Uae6sE6s
its_me

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" 

笔记:

  • 是的,这很复杂...但是,它删除了所有重复项,但保留了每个终端中的时间顺序!
  • HISTIGNORE忽略所有没有参数的命令。这对于某些人来说可能不是理想的,可以忽略不计。


0

它不起作用,因为您忘记了:

 -n   read all history lines not already read from the history file
      and append them to the history list

但是,生效history -n时似乎只是越野车export HISTCONTROL=ignoreboth:erasedups

让我们尝试一下:

$ PROMPT_COMMAND=
$ export HISTCONTROL=ignoreboth:erasedups
$ export HISTFILE=~/.bash_myhistory
$ HISTIGNORE='history:history -w'
$ history -c
$ history -w

在这里,我们打开复制擦除,将历史记录切换到自定义文件,清除历史记录。完成所有命令后,我们将获得空的历史记录文件,而在当前历史记录中只有一个命令。

$ history
$ cat ~/.bash_myhistory
$ history
$ 1  [2019-06-17 14:57:19] cat ~/.bash_myhistory

打开第二个终端并运行这六个命令。之后:

$ echo "X"
$ echo "Y"
$ history -w
$ history
  1  [2019-06-17 15:00:21] echo "X"
  2  [2019-06-17 15:00:23] echo "Y"

现在,您当前的历史记录具有两个命令,历史记录文件具有:

#1560772821
echo "X"
#1560772823
echo "Y"

返回第一个终端:

$ history -n
$ history
1  [2019-06-17 14:57:19] cat ~/.bash_myhistory 
2  [2019-06-17 15:03:12] history -n

呵...没有echo读取任何命令。再次切换到第二个终端,然后:

$ echo "Z"
$ history -w

现在,历史文件为:

#1560772821
echo "X"
#1560772823
echo "Y"
#1560773057
echo "Z"

再次切换到第一个终端:

$ history -n
$ history
  1  [2019-06-17 14:57:19] cat ~/.bash_myhistory 
  2  [2019-06-17 15:03:12] history -n
echo "Z"

您可以看到该echo "Z"命令已合并到history -n

我认为,另一个错误是因为从历史记录中按命令编号而不是按命令时间读取命令。我希望其他echo命令出现在历史上


0

擦除前的空格和尾随的空格不会修剪(如某些语言的chomp)。这是一个错误。同样,擦除不删除所有先前的条目。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.