如何将带有时间戳的命令历史记录连续输出到终端?

如何将带有时间戳的命令历史记录连续输出到终端?

我用一个简单的别名在一个或多个终端窗口中启用命令“跟踪”:

alias trackmi='export PROMPT_COMMAND="history -a; $PROMPT_COMMAND"'

然后我就只是tail -f我的.bash_历史记录在工作区的另一个终端中保存文件以获得即时反馈。我刚刚启用了无限的历史 并更新了我的历史记录格式 ( export HISTTIMEFORMAT="[%F %T] ").bashrc。当然,该history命令会显示时间戳。但是格式历史文件本身是:

#1401234303
alias
#1401234486
cat ../.bashrc 

我怎样才能转换 Unix 时间并将整个命令显示在一行上,就像命令一样history,包括编号:

578  [2014-05-27 19:45:03] alias
579  [2014-05-27 19:48:06] cat ../.bashrc 

...然后遵循。或者想办法将history命令的输出持续输出到终端?

答案1

使用 GNU awk

tail -fn+1 ~/.bash_history | awk '
  /^#/{printf "%-4d [%s] ", ++n, strftime("%F %T", substr($0, 2)); next}; 1'

答案2

这是在分屏 xterm 上运行的最终产品,从基本上 shell 默认到仅使用几个命令:

在此输入图像描述

比屏幕截图中演示的更粗略的方法可能如下所示:

PS1='$( { date ; fc -l -0 ; } >${TGT_PTY} )'$PS1

当您在想要输出的屏幕上实际运行交互式 shell 时,${TGT_PTY}您从命令中得到的任何内容都在哪里。tty或者,实际上,您可以使用任何可写文件,因为它本质上只是文件重定向的目标。

我用普蒂语法为伪终端因为我假设它是某种 xterm,但您也可以轻松地奉献一个 vt - 并且您的流式历史记录始终只需要一个CTRL-ALT-Fn组合键即可。如果是我,我可能会将这两个概念结合起来,并使其成为一个专门的 vtscreen会议tmux……但我离题了。

在新启动的机器上,我会看到/bin/login典型 Linuxgetty控制台上的典型提示。我按CTRL-ALT-F2访问一个不太典型的kmscon控制台,它的行为更像是一个xterm而不是一个tty。我输入命令tty并收到响应/dev/pts/0

通常xterms将单个终端设备复用为多个使用伪终端- 因此,如果您要在 X11 中通过在终端选项卡或窗口之间切换来执行类似的操作,您可能也会收到类似的输出/dev/pts/[0-9]*。但是通过组合键访问的虚拟终端控制台CTRL-ALT-Fn是真正的终端设备,因此有自己的/dev/tty[0-9]*名称。

这就是为什么在登录到控制台 2 后,当我tty在提示符下键入时,响应为 ,/dev/pts/0但当我在控制台 1 上执行相同操作时,输出为/dev/tty1。无论如何,回到控制台 2,我会执行以下操作:

bash
PS1='$( { date ; fc -l -0 ; } >/dev/tty1 )'$PS1

没有明显的效果。我继续输入更多命令,然后CTRL-ALT-F1再次按 切换到控制台 1。我在那里发现了<date_time>\n<hist#>\t<hist_cmd_string>与我在控制台 2 上输入的每个命令类似的重复条目。

不过,除非直接写入终端设备,另一种选择可能如下所示:

TGT_PTY=
mkfifo ${TGT_PTY:=/tmp/shell.history.pipe}
{   echo 'OPENED ON:'
    date
} >${TGT_PTY}

然后也许...

less +F ${TGT_PTY}

粗略的提示命令不符合您的规格 - 没有格式字符串date,也没有格式选项fc- 但其机制并不需要太多:每次您的提示呈现最后一个历史命令时,当前日期和时间都会写入${TGT_PTY}您指定的文件。就是这么简单。

观看和打印 shell 历史记录是fc其主要目的。它是一个内置的 shell,即使date不是。 Inzsh fc可以提供各种奇特的格式选项,其中一些适用于时间戳。当然,正如您上面提到的,bash'shistory也可以做同样的事情。

为了更清晰的输出,您可以使用我更好解释的技术这里在当前 shell 中设置持久跟踪变量,尽管必须在提示序列内的子 shell 中跟踪并处理它。

这是一种根据您的规格进行格式化的便携式方法:

_HIST() { [ -z ${_LH#$1} ] ||
    { date "+${1}%t[%F %T]"
      fc -nl -0 
    } >${TGT_PTY}
    printf "(_LH=$1)-$1"
}

: "${_LH=0}"
PS1='${_LH##*[$(($(_HIST \!)))-9]}'$PS1

我实施最后的历史记录计数器$_LH仅跟踪最新更新,因此您不会两次写入相同的历史命令 - 例如仅用于按 Enter 键。要使变量在当前 shell 中递增,以便即使在子 shell 中调用该函数,它仍保留其值,需要进行一些争论 - 这在关联

它的输出看起来像<hist#>\t[%F %T]\t<hist_cmd>\n

但这只是完全便携的版本。有了bash它,可以用更少的资源并仅实现 shell 内置命令来完成 - 当您认为这是一个每次按 都会运行的命令时,这可能是理想的[ENTER]。这里有两种方法:

_HIST() { [ -z ${_LH#$1} ] || {
        printf "${1}\t[%(%F %T)T]"
        fc -nl -0
    } >${TGT_PTY}
    printf "(_LH=$1)-$1"
}
PROMPT_COMMAND=': ${_LH=0};'$PROMPT_COMMAND
PS1='${_LH##*[$(($(_HIST \!)))-9]}'$PS1

或者,使用bash的命令,您可以按如下方式history定义该函数:_HIST

_HIST() { [ -z ${_LH#$1} ] || 
        HISTTIMEFORMAT="[%F %T]<tab>" \
        history 1 >${TGT_PTY}
    printf "(_LH=$1)-$1"
}

任一方法的输出也类似于:<hist#>\t[%F %T]\t<hist_cmd>\n尽管该history方法包含一些前导空格。尽管如此,我相信该history方法的时间戳会更准确,因为我认为他们不需要等待引用的命令完成才能获取其时间戳。

只要您以某种方式过滤流,您就可以在这两种情况下避免跟踪任何状态uniq- 就像mkfifo我之前提到的那样。

但是在提示中这样做意味着它总是仅在需要时仅通过更新提示的操作进行更新。这很简单。

您也可以做一些与您正在做的事情类似的事情tail,但是设置

HISTFILE=${TGT_PTY}

答案3

请随意使用格式,但这(我相信)可以满足您的要求...保存到路径中的某个位置,使其可执行并享受:

#!/bin/bash
count=$(  echo "scale=0 ; $(cat ~/.bash_history | wc -l ) / 2" | bc -l )
tail -f ~/.bash_history | awk -v c=$count '{if($1 ~/^#/){gsub(/#/, "", $1);printf "%s\t", c; "date \"+%F %T\" --date @" $1 | getline stamp; printf "[%s]\t",stamp;c++}else{print $0}}'

我确信它可以优化,但你明白了。

简要说明:由于 ~/.bash_history 不跟踪计数,因此我们首先确定条目数。然后使用 awk 魔法来获得正确的格式,并跟踪条目的数量。

相关内容