如何删除.bash_history中的重复项并保留顺序?


60

我非常喜欢使用control+r递归搜索命令历史记录。我发现了一些喜欢使用的不错的选择:

# ignore duplicate commands, ignore commands starting with a space
export HISTCONTROL=erasedups:ignorespace

# keep the last 5000 entries
export HISTSIZE=5000

# append to the history instead of overwriting (good for multiple connections)
shopt -s histappend

对我来说,唯一的问题是erasedups仅擦除连续的重复项-因此使用以下命令字符串:

ls
cd ~
ls

ls命令实际上将被记录两次。我考虑过定期运行cron:

cat .bash_history | sort | uniq > temp.txt
mv temp.txt .bash_history

这将实现删除重复项,但是不幸的是,该顺序将无法保留。如果我不sort首先归档文件,则认为uniq无法正常工作。

如何删除.bash_history中的重复项并保留顺序?

额外信用:

.bash_history通过脚本覆盖文件是否存在任何问题?例如,如果您删除了一个Apache日志文件,我认为您需要发送一个nohup / reset信号,kill以使其刷新与文件的连接。如果.bash_history文件是这种情况,也许我可以以某种方式使用它ps来检查并确保在运行过滤脚本之前没有连接的会话?


3
尝试一下,ignoredups而不是花erasedups一会儿,看看它如何为您工作。
2012年

1
我不认为bash拥有历史文件的打开文件句柄-它会在需要时读取/写入它,因此(注意- 应该 -我尚未测试)应该可以安全地从其他位置覆盖它。
D_Bye 2012年

1
我刚刚在您的问题的第一句话中学到了新东西。好招!
里卡多

我找不到该history命令所有选项的手册页。我应该在哪里看?
乔纳森·哈特利

历史记录选项位于“ man bash”中,搜索“ shell内置命令”部分,然后在其下方搜索“ history”。
乔纳森·哈特利

Answers:


36

整理历史

此命令的工作方式类似于sort|uniq,但将行保持在适当的位置

nl|sort -k 2|uniq -f 1|sort -n|cut -f 2

基本上,在每行前添加其编号。sort|uniq-ing 之后,所有行均按照其原始顺序(使用行号字段)进行排序,然后从行中删除行号字段。

该解决方案的缺陷在于,不确定哪一类相等的线将使它出现在输出中,因此它在最终输出中的位置是不确定的。但是,如果应选择最新的代表,则可以sort通过第二个键输入:

nl|sort -k2 -k 1,1nr|uniq -f1|sort -n|cut -f2

管理.bash_history

要重新读取和写回历史记录,可以分别使用history -ahistory -w


6
使用shell工具实现的decorate-sort-undecorate版本。真好
ire_and_curses 2012年

使用时sort-r开关始终颠倒排序顺序。但这不会产生您所想到的结果。sort认为两次出现ls的结果相同,即使反转,最终顺序也取决于排序算法。但是,请参阅我的更新以了解其他想法。
artistoex 2012年

1
如果您不想修改.bash_history,则可以将以下内容放在.bashrc中:alias history ='history | 排序-k2 -k 1,1nr | uniq -f 1 | 排序-n”
弥敦道

什么是nl在每行代码的开始?不是history吗?
AL

1
@AL nl添加行号。整个命令解决了一个普遍的问题:在保留顺序的同时删除重复项。输入是从stdin中读取的。
artistoex

48

因此,在被重复的内容烦恼之后,我一直在寻找相同的东西,并且发现如果我用以下命令编辑〜/ .bash_profile(Mac):

export HISTCONTROL=ignoreboth:erasedups

它可以完全满足您的要求,只保留最新命令。ignoreboth实际上就像是在做,ignorespace:ignoredups并伴随着erasedups完成工作。

至少在我使用bash的Mac终端上,这项工作非常完美。在askubuntu.com上找到它。


10
这应该是正确的答案
MitchBroadhead '16

在Max OS X Yosemite和Ubuntu 14_04上进行了测试
Ricardo

1
同意@MitchBroadhead。这可以解决bash本身的问题,而无需外部cron-job。在ubuntu 17.04和16.04 LTS上测试了它
Georg Jung

也可以在OpenBSD上使用。它只会删除附加到历史文件的任何命令的复制,这对我来说很好。当我输入以前作为重复项存在的命令时,它具有缩短历史记录文件的有趣效果。现在,我可以将历史记录文件的最大长度缩短。
WeakPointer18年

1
这只会忽略重复的连续命令。如果两个给定的命令之间反复交替,你的bash的历史,将填补了重复
Dylanthepiguy

16

在野外发现了此解决方案并进行了测试:

awk '!x[$0]++'

第一次看到行的特定值($ 0)时,x [$ 0]的值为零。
零的值与反转!并变为1。
计算结果为1的语句将导致默认操作,即print。

因此,第一次$0看到特定内容时,将其打印出来。

每次下一次(重复)时,的值x[$0]都会增加,
其否定值为零,并且不会输出计算结果为零的语句。

要保留最后一个重复的值,请反转历史记录并使用相同的awk:

awk '!x[$0]++' ~/.bash_history                 # keep the first value repeated.

tac ~/.bash_history | awk '!x[$0]++' | tac     # keep the last.

哇!就是这样。但是,它除去了我猜想除第一次出现的所有内容。在运行此命令之前,我已使用Sublime Text颠倒了行的顺序。现在,我将再次反转它以得到干净的历史记录,仅保留最后一次出现的所有重复项。谢谢。
trss

看看我的答案!
阿里·沙基巴

简洁,通用的答案(不限于历史用例),而无需启动bazilion子流程;-)
JepZ

9

扩展克莱顿答案:

tac $HISTFILE | awk '!x[$0]++' | tac | sponge $HISTFILE

tac反转文件,请确保已安装,moreutils以便可以sponge使用,否则请使用临时文件。


1
对于Mac上的用户,请使用brew install coreutils,并注意所有GNU utils都有一个g前缀,以避免与BSD内置Mac命令混淆(例如gsed为GNU而sed为BSD)。所以用gtac
特拉斯顿2015年

我需要历史记录-c和历史记录-r才能使用历史记录
drescherjm

4

这些将保留最后重复的行:

ruby -i -e 'puts readlines.reverse.uniq.reverse' ~/.bash_history
tac ~/.bash_history | awk '!a[$0]++' | tac > t; mv t ~/.bash_history

明确地说,我是否理解您在此处显示了两个(出色的)解决方案,而用户只需要执行其中一个解决方案,对吗?是红宝石还是Bash?
乔纳森·哈特利

3

这是旧文章,但对于希望打开多个终端并在窗口之间同步历史记录但又不重复的用户来说,这是一个永恒的问题。

我在.bashrc中的解决方案:

shopt -s histappend
export HISTCONTROL=ignoreboth:erasedups
export PROMPT_COMMAND="history -n; history -w; history -c; history -r"
tac "$HISTFILE" | awk '!x[$0]++' > /tmp/tmpfile  &&
                tac /tmp/tmpfile > "$HISTFILE"
rm /tmp/tmpfile
  • histappend选项将缓冲区的历史记录添加到历史记录文件($ HISTFILE)的末尾
  • ignoreboth和擦除表均会防止重复的条目保存在$ HISTFILE中
  • 提示命令更新历史记录缓存
    • history -n 自最后一次回车以来,从$ HISTFILE读取可能在其他终端中发生的所有行
    • history -w 将更新的缓冲区写入$ HISTFILE
    • history -c 擦除缓冲区,因此不会发生重复
    • history -r 重新读取$ HISTFILE,追加到现在的空白缓冲区
  • awk脚本存储它遇到的每一行的第一次出现。tac反转它,然后反转它,以便可以将其与历史中最新的命令一起保存
  • rm / tmp文件

每次打开新的外壳程序时,历史记录都会被清除,并且每次Enter在不同的外壳程序/终端窗口中按下键时,都会从文件中更新此历史记录。



如果“忽略和擦除会阻止保存重复项”,那么为什么还需要执行“ awk”命令从文件中删除重复项?是否因为“忽略和擦除”仅阻止保存连续的重复?很抱歉,我只是想了解一下。
乔纳森·哈特利

1
擦除仅擦除连续的重复项。您是正确的,awk命令复制了deletedupes命令,使它多余。
–frogfrog

谢谢,这让我很清楚发生了什么事。
乔纳森·哈特利

0

唯一地记录每个新命令是很棘手的。首先,您需要添加 ~/.profile或类似内容:

HISTCONTROL=erasedups
PROMPT_COMMAND='history -w'

然后,您需要添加到~/.bash_logout

history -a
history -w

您能帮我理解为什么注销时需要先将未写入的历史记录附加到历史记录文件中,然后才能重写整个历史记录文件吗?您不能只写没有“追加”的整个文件吗?
乔纳森·哈特利
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.