![Bash:将历史 -a 添加到 PROMPT_COMMAND 会导致最后一个命令的挂起时间计算不正确](https://linux22.com/image/185525/Bash%EF%BC%9A%E5%B0%86%E5%8E%86%E5%8F%B2%20-a%20%E6%B7%BB%E5%8A%A0%E5%88%B0%20PROMPT_COMMAND%20%E4%BC%9A%E5%AF%BC%E8%87%B4%E6%9C%80%E5%90%8E%E4%B8%80%E4%B8%AA%E5%91%BD%E4%BB%A4%E7%9A%84%E6%8C%82%E8%B5%B7%E6%97%B6%E9%97%B4%E8%AE%A1%E7%AE%97%E4%B8%8D%E6%AD%A3%E7%A1%AE.png)
为了计算最后一个命令的挂起时间,我使用以下代码(code #1
):
#
# Taken from https://stackoverflow.com/a/34812608/9881330
# Slightly modified.
#
function timer_now {
date +%s%N
}
function timer_start {
timer_start=${timer_start:-$(timer_now)}
}
function timer_stop {
local delta_us=$((($(timer_now) - $timer_start) / 1000))
local us=$((delta_us % 1000))
local ms=$(((delta_us / 1000) % 1000))
local s=$(((delta_us / 1000000) % 60))
local m=$(((delta_us / 60000000) % 60))
local h=$((delta_us / 3600000000))
# Goal: always show around 3 digits of accuracy
if ((h > 0)); then timer_show=${h}h${m}m
elif ((m > 0)); then timer_show=${m}m${s}s
elif ((s >= 10)); then timer_show=${s}.$((ms / 100))s
elif ((s > 0)); then timer_show=${s}.$(printf %03d $ms)s
elif ((ms >= 100)); then timer_show=${ms}ms
elif ((ms > 0)); then timer_show=${ms}.$((us / 100))ms
else timer_show=${us}us
fi
unset timer_start
}
set_prompt () {
PS1="\n\$?"
timer_stop
PS1+=" / $timer_show"
PS1+="\n\n\[\e]0;\w\a\]\[\e[32m\]\u@\h \[\e[33m\]\w\[\e[0m\]\n\$ "
}
trap 'timer_start' DEBUG
PROMPT_COMMAND='set_prompt'
而且效果非常好:
$ gcc
gcc: fatal error: no input files
compilation terminated.
1 / 108ms
$ date
Sun Dec 27 14:53:10 RTZST 2020
0 / 89.3ms
然而,要解决这个history is lost after ssh connection is reset by peer
问题(Bash:ssh 连接被对等方重置后历史记录丢失:如何保存历史记录?),在前面的代码(code #1
)之后我添加了这段代码(code #2
):
PROMPT_COMMAND="${PROMPT_COMMAND:+${PROMPT_COMMAND/%;};}history -a"
现在,最后一个命令的挂起时间计算不正确(而命令在视觉上像往常一样以毫秒为单位运行):
$ gcc
gcc: fatal error: no input files
compilation terminated.
1 / 1.990s
$ date
Sun Dec 27 14:56:07 RTZST 2020
0 / 3.586s
问题:为什么添加code #2
aftercode #1
会code #1
产生错误的结果?怎么修?
答案1
事件链(假设读者知道什么${var:-someval}
有效):
- 您输入一些命令。
- 当你按下回车键时,
DEBUG
trap 就会被执行。timer_start=${timer_start:-$(timer_now)}
- 然后执行命令。
- 然后
set_prompt
就被执行了。- 巴拉巴拉,然后
unset timer_start
- 计时器重新启动。
- 然后
DEBUG
之前执行traphistory -a
timer_start=${timer_start:-$(timer_now)}
- 然后
history -a
就被执行了。 - 时间流逝,您输入命令。
- 然后
DEBUG
trap 在你输入命令之前执行。timer_start=${timer_start:-$(timer_now)}
- 但
timer_start
已经设置了,所以什么也没有发生。
- 然后你的命令就会被执行。
set_prompt
超长,因为它测量的是距离上次prompt_command
执行history -a
的时间。
history -a
例如,在运行之前执行或在运行时set_prompt
删除陷阱。DEBUG