如何將帶有時間戳的命令歷史記錄連續輸出到終端?
我使用一個簡單的別名來啟用一個或多個終端視窗中的命令“跟踪”:
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 上進行一次screen
或tmux
會議……但我離題了。在新啟動的機器上,我會看到
/bin/login
典型 Linuxgetty
控制台上的典型提示。我按下CTRL-ALT-F2
以訪問一個不太典型的kmscon
控制台,它的行為更像是xterm
a 而不是 atty
。我輸入命令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}