在两个时间点之间查找Bash历史记录

6
我想要做类似于以下的操作:
history 2960-2966

希望得到这种输出结果:

2960  2013-12-09 20:59:35 bundle update
2961  2013-12-09 21:00:08 git st
2962  2013-12-09 21:00:12 git add .
2963  2013-12-09 21:01:19 git st
2964  2013-12-09 21:01:43 git log
2965  2013-12-09 21:02:45 git st
2966  2013-12-09 21:02:57 git reset HEAD Gemfile.lock

这个在bash shell中可行吗?

更新:我的问题是关于查看那七行历史记录,并不是关于日期本身;只是碰巧我在我的.bash_profile中有一个HISTTIMEFORMAT指令来提供日期和时间戳。如果我将其删除,那么我希望history 2960-2966或其他命令可以生成以下输出:

2960  bundle update
2961  git st
2962  git add .
2963  git st
2964  git log
2965  git st
2966  git reset HEAD Gemfile.lock

我希望能够显示所需的历史行,最好能够显示我在.bash_profile中指定的任何自定义内容。
为什么要这样做?一个非常简单的用例是:我执行像“history | grep 'bundle update'”这样的命令得到了一个不错的结果,然后想要查看从那个点开始以及往后几行的历史记录,或者可能是该点周围的历史记录。

你应该能够使用Git标签来完成这个任务。请参考页面底部。 - Brian
正如@Tharanga Abeyseela在他的答案中指出的那样,由于我的.bash_profile中有“HISTTIMEFORMAT ='%F%T'”指令,我在历史记录中有一个日期显示。 - Purplejacket
1
嘿@Brian,这个问题根本不是关于git的,那些只是我历史记录中的那一点。 - Purplejacket
1
有一种在mysql中存储历史会话的方法(每个shell会话作为一个数据库条目),还有一种在单独文件中存储整个shell会话的方法(可以进行回放)。 - F. Hauri - Give Up GitHub
我猜你首先要设置历史记录以存储时间戳,http://larsmichelsen.com/open-source/bash-timestamp-in-bash-history/ 你有吗? - fedorqui
我希望人们不要被我的输出中的YYYY-MM-DD字符串分心。那只是因为我在我的.bash_profile中有HISTTIMEFORMAT。我希望我的问题的解决方案能够在历史命令由格式选项引导的情况下正常工作。 - Purplejacket
7个回答

3
您可以使用sed命令。例如:
history | sed -n '2960,2966p'

history命令的输出中打印行号2960到2966。

引用help history

If the $HISTTIMEFORMAT variable is set and not null, its value is used
as a format string for strftime(3) to print the time stamp associated
with each displayed history entry.  No time stamps are printed otherwise.

您可以通过以下方式设置时间戳的格式,以获取所需的格式:
export HISTTIMEFORMAT="%F %T "

由于默认情况下history文件只在会话关闭时才会写入,所以您需要执行以下操作:

history -w

为了在当前会话中更新历史文件。

这个 sed 表达式对我不起作用。根本没有任何输出显示,而我在我的答案中定义的 histrange 函数确实可以工作(在 Ubuntu 12.04 上)https://dev59.com/6XrZa4cB1Zd3GeqPzjIS#20604325。你能解释一下这个 sed 表达式应该如何工作吗? - Digital Trauma
@DigitalTrauma 这个解决方案在我使用的OSX 10.9.1 (Mavericks)上有效。您知道它为什么不能在Ubuntu上使用吗?我喜欢devnull的解决方案,因为我可能甚至只需要记住它,但它不是通用的吗? - Purplejacket
1
@Purplejacket - history 展示了一个循环缓冲区 - 一旦缓冲区满了,旧的条目将被新的条目覆盖。这意味着如果缓冲区已经包装(在Ubuntu上似乎是在1000处),则 history 输出可能会列出历史缓冲区的条目834-1833。因此,history 输出的行号不一定对应于历史记录条目编号。devnull 的 sed 解决方案是基于行号(通过计算输出行数)。我的解决方案是基于实际的 history 条目编号。 - Digital Trauma
@Purplejacket 你可以尝试输入以下命令:history | sed -n '/^\s*2960 /,/^\s*2966 /p' - devnull

3

fc -l 2960 2966

这个命令适用于OS 10.6.X,可以提供你所需的确切内容。希望这能帮到你!


这非常简单而且好用。我可以确认它在Linux Mint(Ubuntu的衍生版)上运行正常。你知道它是否适用于其他Unix系统吗? - Purplejacket
1
据我所知,fc 很普遍可用:http://alasir.com/books/bsd/211-214.html - Purplejacket
@Purplejacket 谢谢,是的,只要fc可用。 - Devesh
还有其他人关心它为什么能够工作,或者它到底做了什么吗?务实是一回事,但是即使一个优秀的解决方案也会在没有明确的推理的情况下建立起一种货物崇拜行为。 - Romain Valeri
fc 是一个 Bash 命令实用程序(也可用于其他 shell)。您可以使用它来编辑和重新运行命令,如果使用 -l 标志,则会将其打印到 stdout(而不是打开默认编辑器)。您可以使用数字作为索引来指定起始点和结束点。负数被解释为偏移量 fc -l -5 最近运行的 5 个命令。 - Jason

1
根据您在“为什么想要这个”的陈述,给出以下内容:
history | grep -A 4 -B 3 whatyousearchfor

这应该会给你想要的内容。

whatyousearchfor 可以是您最初搜索的术语,也可以是首先使用不带上下文行的 grep 命令,然后使用历史行号作为 whatyousearchfor


1
我们希望显示从startend(包括startend)的条目。
内置于$HISTCMD变量提供了历史缓冲区中最新条目的编号。
history命令需要一个n参数,用于显示最后n个条目。因此,在使用n参数显示条目时,起始编号为:
start = $HISTCMD - n

重新以n为单位排列,我们得到了这个表达式,可以传递给history:
n = $HISTCMD - start

现在我们只想显示从startend(包括start和end)的条目,即end-start+1条目。我们可以使用head来仅显示这些条目。
将其封装成一个易于使用的函数:
$ function histrange {
>     history $(($HISTCMD - $1)) | head -n$(($2 - $1 + 1))
> }
$ histrange 2097 2100
 2097  2013-12-15 21:54:02 man ls
 2098  2013-12-15 21:54:06 ls
 2099  2013-12-15 21:54:10 man df
 2100  2013-12-15 21:54:15 df -k .
$ 

1
这里有一种使用 的方法: $ function histrange { > history | grep -E -A $(($2 - $1)) "^[[:space:]]*$1[[:space:]]" > } $ histrange 2100 2105 2100 2013-12-15 21:54:15 df -k . 2101 2013-12-15 21:54:18 history 2102 2013-12-15 21:54:40 histrange 2097 2100 2103 2013-12-15 21:55:30 man grep 2104 2013-12-15 22:35:33 man history 2105 2013-12-15 22:38:35 export HISTTIMEFORMAT="%F %T " $
函数 histrange 可以实现此功能。
我们确保正则表达式锚定到行的开头,在开始数字之前有零个或多个空格,并在开始数字后有一个空格,这样我们只精确匹配开始数字和历史记录条目编号,而不匹配其他内容。我们使用 -A 选项来显示与匹配相对应的 $end - $start 行。

0
$> export HISTTIMEFORMAT="    %F    %T     "

然后:
$> history | awk '{if (1070<=$1 && $1<=1075) {print}}'

这几乎是devnull的响应。 以前从未考虑过这样的事情,但我喜欢这个想法,认为它对我也有用。我从内置的bash帮助中获得了一个近似的答案。

$> help history

如果$HISTTIMEFORMAT变量被设置并且不为空,它的值将用作strftime(3)的格式字符串,以打印与每个显示的历史条目相关联的时间戳。否则不打印时间戳。

然后。
$> man 3 srtftime

0
尝试以下命令。这将显示从2960到2966的范围。
fc -ln 2960-2966

执行它们,您可以尝试这个。
eval "$(fc -ln 2960 2966)"

希望这有所帮助。

Tharanga Abeyseela


我尝试使用“fc”命令,它显示了来自2960的历史行,但没有在2966处停止。 - Purplejacket
这不是一个坏主意,但它会找到数字2966的其他出现。 - Purplejacket
我经常使用 history | tail -n 50 命令,但我想不出如何将其适应于历史记录中的两个特定点。 - Purplejacket
另一个选项是使用带有grep的正则表达式模式。 - Tharanga Abeyseela
尝试这个命令 history | awk {'print $1 " " $2'}| grep -B6 ^2966 | head -n 6 - Tharanga Abeyseela
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接