我目前使用它在 bash 提示符中显示当前时间:
PS1=\[\e[0;32m\]\t \W>\[\e[1;37m\]
20:42:23 ~>
是否可以显示自上次提示以来经过的时间?例如:
00:00:00 ~> sleep 10
00:00:10 ~> sleep 20
00:00:20 ~>
答案1
一种方法是使用 bash 的 PROMPT_COMMAND 功能来执行修改 PS1 的代码。下面的函数是我原始提交的更新版本;这个使用了两个较少的环境变量,并在它们前面加上“_PS1_”前缀,以避免破坏现有变量。
prompt_command() {
_PS1_now=$(date +%s)
PS1=$( printf "\[\e[0;32m\]%02d:%02d:%02d \W>\[\e[1;37m\] " \
$(( ( _PS1_now - _PS1_lastcmd ) / 3600)) \
$(( (( _PS1_now - _PS1_lastcmd ) % 3600) / 60 )) \
$(( ( _PS1_now - _PS1_lastcmd ) % 60)) \
)
_PS1_lastcmd=$_PS1_now
}
PROMPT_COMMAND='prompt_command'
_PS1_lastcmd=$(date +%s)
将其放入您的 .bash_profile 中即可启动。
请注意,您必须快速键入才能使sleep
参数与提示参数匹配 - 时间实际上是提示之间的差异,包括您键入命令所需的时间。
00:00:02 ~> sleep 5 ## here I typed really quickly
00:00:05 ~> sleep 3 ## here I took about 2 seconds to enter the command
00:00:10 ~> sleep 30 ## more slow typing
00:01:35 ~>
后期补充:
基于 @Cyrus 现已删除的答案,这是一个不会因额外变量而使环境混乱的版本:
PROMPT_COMMAND='
_prompt(){
PROMPT_COMMAND="${PROMPT_COMMAND%-*}-$SECONDS))\""
printf -v PS1 "\[\e[0;32m\]%02d:%02d:%02d \W>\[\e[1;37m\] " \
"$(($1/3600))" "$((($1%3600)/60))" "$(($1%60))"
}; _prompt "$((SECONDS'"-$SECONDS))\""
额外后期添加:
从 bash 版本 4.2 开始( echo $BASH_VERSION
),可以date
使用新的 printf 格式字符串来避免外部调用;将零件替换$(date +%s)
为$(printf '%(%s)T' -1)
.从 4.3 版本开始,你可以省略-1
参数来依赖“无参数意味着现在“ 行为。
答案2
PS1[3]=$SECONDS
PS1='${PS1[!(PS1[1]=!1&(PS1[3]=(PS1[2]=$SECONDS-${PS1[3]})/3600))
]#${PS1[3]%%*??}0}$((PS1[3]=(PS1[2]/60%60), ${PS1[3]})):${PS1[1
]#${PS1[3]%%*??}0}$((PS1[3]=(PS1[2]%60), ${PS1[3]})):${PS1[1
]#${PS1[3]%%*??}0}$((PS1[3]=(SECONDS), ${PS1[3]})):'$PS1
这通过计算处理格式化 - 因此,虽然它确实扩展了几次,但它不执行任何子 shell 或管道。
它只是将其$PS1
视为数组并使用较高的索引来存储/计算提示之间的任何/所有必要状态。不影响其他 shell 状态。
00:00:46:[mikeserv@desktop tmp]$
00:00:01:[mikeserv@desktop tmp]$
00:00:00:[mikeserv@desktop tmp]$
00:00:01:[mikeserv@desktop tmp]$
00:00:43:[mikeserv@desktop tmp]$ sleep 10
00:00:33:[mikeserv@desktop tmp]$ sleep 10
00:00:15:[mikeserv@desktop tmp]$
00:00:15:[mikeserv@desktop tmp]$
00:00:02:[mikeserv@desktop tmp]$
00:02:27:[mikeserv@desktop tmp]$
也许我可以把它分解一点...
首先,保存 的当前值$SECONDS
:
PS1[3]=$SECONDS
接下来,$PS1[0]
以始终设置正确值的方式定义自递归,$PS1[1-3]
同时进行自引用。要获得这一部分,您必须考虑 shell 数学表达式的求值顺序。最重要的是,shell 数学始终是 shell 数学的最后一项任务。首先,shell 扩展值。通过这种方式,您可以在使用 赋值后在数学表达式中引用 shell 变量的旧值$
。
首先是一个简单的例子:
x=10; echo "$(((x+=5)+$x+x))" "$x"
40 15
shell 将通过首先替换使用美元符号引用的$x
位置的值来计算该语句$
,因此表达式变为:
(x+=5)+10+x
...然后 shell 将 的值加 5 $x
,然后将整个表达式扩展为x+10+x
,同时仅保留引用变量中实际分配的值。所以数学表达式的扩展值为 40,但最终值为$x
15。
这很大程度上$PS1
也是等式的工作原理,只不过数组索引中利用了更进一步的数学扩展/评估。
PS1='${PS1[!(PS1[1]=!1&(...))]#...}...'
我不太确定为什么我选择PS1[1]=!1
在那里使用 - 我想这可能只是愚蠢的美学 - 但这会$PS1[1]
在扩展它以进行参数替换时分配 0 。 0 和其他任何值的按位 AND 的值将始终为 0,但当&&
最左边的主值为 0 时,它不会像布尔值那样短路,因此每次仍会计算括号表达式。当然,这很重要,因为第一个省略号是$PS1[2,3]
设置初始值的地方。
无论如何,$PS1[1]
即使在提示抽奖之间被篡改,这里也保证为 0。括号里面有...
PS1[3]=(PS1[2]=$SECONDS-${PS1[3]})/3600
...被分配和$PS1[2]
的差,并被分配该值和 3600 的商。所有值均在此处初始化。所以:$PS1[3]
$SECONDS
$PS1[3]
${PS1[1]#${PS1[3]%%*??}0}
...如果其中至少有两位数字,$PS1[3]
则内部扩展为空,并且因为我们知道$PS1[1]
为 0,则 if$PS1[3]
可以替换为空,因此$PS1[1]
else 也可以扩展为它的值。以这种方式,每次$PS1[3]
赋值迭代只有单个数字值将扩展前导零,并且$PS1[3]
其本身立即以 60 为模扩展,同时同时为每个小时、分钟、秒分配下一个连续较小的值。
冲洗并重复,直到最后一次迭代$PS1[3]
被当前值覆盖,以便下次绘制提示时$SECONDS
可以再次进行比较。$SECONDS
答案3
到目前为止我发现的最好的解决方案是:https://github.com/jichu4n/bash-command-timer
它[ 1s011 | May 25 15:33:44 BST ]
会在执行命令后在右侧打印经过的时间,因此它不会让您的 PS1 感到混乱。
整个字符串和时间格式是可配置的。甚至颜色和精度也是可配置的。我知道这对于一些极简主义者来说可能有点太多了,但它非常酷。
答案4
自 Bash 以来非常可行4.4,它引入了PS0
环境变量:
export PS0='$(date +%s%03N > ~/.execStart.$$)'
printElapsedCommandTime () {
local execStart execEnd execDur elaStr
if [ -e ~/.execStart.$$ ]; then
read execStart < ~/.execStart.$$ 2>/dev/null
rm -f ~/.execStart.$$
if [ -n "$execStart" ]; then
execEnd=`date +%s%03N`
execDur=$((execEnd-execStart))
printf -v elaStr "Ela %u.%03u" $((execDur / 1000)) $((execDur % 1000))
printf "%*s\e[32;7m%s\e[0m\n" $((COLUMNS-${#elaStr})) "" "$elaStr"
fi
fi
}
export PROMPT_COMMAND='printElapsedCommandTime'
在PS0
(显示后Bash 已读取命令,但是前Bash 已经开始执行它)我们将 的输出保存在临时文件中date +%s%03N
,即纪元秒和纳秒的前 3 位数字:总体效果是保存纪元微秒。
在PROMPT_COMMAND
(评估后Bash 已完成执行命令,但是前Bash 已显示主提示PS1
),我们再次找到 Epoch 微秒,并从中减去保存在临时文件中的测量值。
我们在右侧的单独一行中显示经过的时间。下层printf
有点棘手:
- 格式说明符
%*s
消耗二参数,一个整数和一个字符串;前者表示打印后者时要使用多少列。我们提供的两个参数是 N =$((COLUMNS-${#elaStr}))
和空字符串""
;效果是打印N个空格。 \e[32;7m
7
请求带有绿色 (“ ” ) 背景的反视频 ( “32
”) 文本。%s
是消耗我们$elaStr
字符串的东西。\e[0m
将字体颜色重置为正常颜色。
重击5.0引入了EPOCHREALTIME
环境变量,这避免了两次date(1)
调用。EPOCHREALTIME
是微秒粒度的小数秒;删除.
后,它与 的输出相同date +%s%03N
,因此我们只需进行两个小更改即可获得改进的解决方案:
export PS0='echo ${EPOCHREALTIME/./} > ~/.execStart.$$)' ### change here...
printElapsedCommandTime () {
local execStart execEnd execDur elaStr
if [ -e ~/.execStart.$$ ]; then
read execStart < ~/.execStart.$$ 2>/dev/null
rm -f ~/.execStart.$$
if [ -n "$execStart" ]; then
execEnd=`${EPOCHREALTIME/./} ### ...and here
execDur=$((execEnd-execStart))
printf -v elaStr "Ela %u.%03u" $((execDur / 1000)) $((execDur % 1000))
printf "%*s\e[32;7m%s\e[0m\n" $((COLUMNS-${#elaStr})) "" "$elaStr"
fi
fi
}
export PROMPT_COMMAND='printElapsedCommandTime'