Bash

如何將帶有時間戳的命令歷史記錄連續輸出到終端?

  • October 10, 2020

我使用一個簡單的別名來啟用一個或多個終端視窗中的命令“跟踪”:

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

然後,我只需將tail -f我的*.bash_history文件放在工作區的另一個終端中即可獲得即時回饋。我剛剛啟用了無限歷史記錄並在.bashrc*中 更新了我的歷史記錄格式 ( export HISTTIMEFORMAT="[%F %T] ") 。當然,該命令會顯示時間戳。但歷史文件本身的格式是: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到終端?

使用 GNU awk

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

這是在分屏 xterm 上執行的最終產品,從基本上 shell 預設設置為僅使用幾個命令:

在此處輸入圖像描述

比螢幕截圖中展示的更粗略的方法可能如下所示:

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

在您想要輸出的螢幕上實際執行互動式 shell 時,${TGT_PTY}您從命令中得到什麼。tty或者,實際上,您可以使用任何可寫文件,因為它本質上只是文件重定向的目標。

我將pty語法用於偽終端,因為我假設它是某種 xterm,但您也可以輕鬆地使用 vt - 而且您的流式傳輸歷史始終只是一個CTRL-ALT-Fn關鍵組合。如果是我,我可能會將這兩個概念結合起來,並在專用 vt 上進行一次screentmux會議……但我離題了。

在新啟動的機器上,我會看到/bin/login典型 Linuxgetty控制台上的典型提示。我按下CTRL-ALT-F2以訪問一個不太典型的kmscon控制台,它的行為更像是xterma 而不是 a tty。我輸入命令tty並收到響應/dev/pts/0

通常 xterms 使用偽終端將單個終端設備多路復用為多個- 因此,如果您通過在終端選項卡或視窗之間切換在 X11 中做類似的事情,您可能也會收到類似/dev/pts/[0-9]*的輸出。但是使用CTRL-ALT-Fn組合鍵訪問的虛擬終端控制台是真正的(er)終端設備,因此會收到它們自己的/dev/tty[0-9]*名稱。

這就是為什麼當我tty在提示符下輸入登錄到控制台 2 後,響應是/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’s 的主要目的。它是一個內置的外殼,即使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

我實現了last_history計數器$_LH,它只跟踪最新的更新,這樣你就不會兩次寫出相同的歷史命令——例如只是為了按下輸入鍵。要在目前 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’shistory命令,您可以_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}

引用自:https://unix.stackexchange.com/questions/132293