似乎通过执行PS0和PS1变量中的代码(据我所知,它们是在运行提示命令之前和之后求值的),应该可以记录每个运行命令的时间并将其显示在提示符中。大概是这样的:
user@machine ~/tmp
$ sleep 1
user@machine ~/tmp 1.01s
$
然而,我很快就在PS0中遇到了录制时间的问题,因为这样的东西不能工作:
PS0='$(START=$(date +%s.%N))'
据我所知,START
赋值发生在子shell中,因此它在外壳中不可见。您将如何处理此问题?
发布于 2019-09-27 20:00:00
我正在寻找一个不同问题的解决方案,并遇到了这个问题,并决定拥有一个听起来很酷的功能。除了我为其他问题开发的解决方案之外,使用@Scheff的优秀答案作为基础,我想出了一个更优雅和功能齐全的解决方案。
首先,我创建了几个函数来读取/写入内存中的时间。写入共享内存文件夹会阻止磁盘访问,并且如果由于某种原因未清除文件,则在重新启动时也不会持续存在
function roundseconds (){
# rounds a number to 3 decimal places
echo m=$1";h=0.5;scale=4;t=1000;if(m<0) h=-0.5;a=m*t+h;scale=3;a/t;" | bc
}
function bash_getstarttime (){
# places the epoch time in ns into shared memory
date +%s.%N >"/dev/shm/${USER}.bashtime.${1}"
}
function bash_getstoptime (){
# reads stored epoch time and subtracts from current
local endtime=$(date +%s.%N)
local starttime=$(cat /dev/shm/${USER}.bashtime.${1})
roundseconds $(echo $(eval echo "$endtime - $starttime") | bc)
}
bash_函数的输入是bash PID
这些函数和以下内容将添加到~/.bashrc文件中
ROOTPID=$BASHPID
bash_getstarttime $ROOTPID
它们创建初始时间值并将bash PID存储为可以传递给函数的不同变量。然后将函数添加到PS0和PS1中
PS0='$(bash_getstarttime $ROOTPID) etc..'
PS1='\[\033[36m\] Execution time $(bash_getstoptime $ROOTPID)s\n'
PS1="$PS1"'and your normal PS1 here'
现在,它将在处理终端输入之前在PS0中生成时间,在处理终端输入之后在PS1中再次生成时间,然后计算差值并添加到PS1中。最后,此代码在终端退出时清理存储的时间:
function runonexit (){
rm /dev/shm/${USER}.bashtime.${ROOTPID}
}
trap runonexit EXIT
将所有这些放在一起,加上一些正在测试的额外代码,它看起来如下所示:
重要的部分是以毫秒为单位的执行时间,以及存储在共享存储器中的所有活动终端PID的user.bashtime文件。PID也显示在终端输入之后,正如我在PS0中添加的一样,您可以看到添加和删除的bashtime文件。
PS0='$(bash_getstarttime $ROOTPID) $ROOTPID experiments \[\033[00m\]\n'
发布于 2017-04-04 09:55:17
我把这个当作拼图,并想展示我的拼图的结果:
首先,我摆弄了一下时间测量。date +%s.%N
(我以前没有意识到)是我开始的地方。不幸的是,bash
的算术计算似乎不支持浮点。因此,我选择了其他东西:
$ START=$(date +%s.%N)
$ awk 'BEGIN { printf("%fs", '$(date +%s.%N)' - '$START') }' /dev/null
8.059526s
$
这足以计算时间差。
接下来,我确认了您已经描述的内容:子shell调用阻止shell变量的使用。因此,我想我还可以在哪里存储开始时间,它对于子shell来说是全局的,但足以在多个交互式shell中同时使用。我的解决方案是temp。文件(/tmp
格式)。为了提供一个唯一的名称,我想出了这个模式:/tmp/$USER.START.$BASHPID
。
$ date +%s.%N >/tmp/$USER.START.$BASHPID ; \
> awk 'BEGIN { printf("%fs", '$(date +%s.%N)' - '$(cat /tmp/$USER.START.$BASHPID)') }' /dev/null
cat: /tmp/ds32737.START.11756: No such file or directory
awk: cmd. line:1: BEGIN { printf("%fs", 1491297723.111219300 - ) }
awk: cmd. line:1: ^ syntax error
$
该死的!我又一次被困在了子shell问题中。为了解决这个问题,我定义了另一个变量:
$ INTERACTIVE_BASHPID=$BASHPID
$ date +%s.%N >/tmp/$USER.START.$INTERACTIVE_BASHPID ; \
> awk 'BEGIN { printf("%fs", '$(date +%s.%N)' - '$(cat /tmp/$USER.START.$INTERACTIVE_BASHPID)') }' /dev/null
0.075319s
$
下一步:将其与PS0
和PS1
结合起来。在一个类似的拼图(SO: How to change bash prompt color based on exit code of last command?)中,我已经掌握了“引用地狱”这个词。因此,我应该能够再做一次:
$ PS0='$(date +%s.%N >"/tmp/${USER}.START.${INTERACTIVE_BASHPID}")'
$ PS1='$(awk "BEGIN { printf(\"%fs\", "$(date +%s.%N)" - "$(cat /tmp/$USER.START.$INTERACTIVE_BASHPID)") }" /dev/null)'"$PS1"
0.118550s
$
啊哈。它开始起作用了。因此,只有一个问题-为INTERACTIVE_BASHPID
的初始化找到正确的启动脚本。我找到了~/.bashrc
,它似乎是最适合这一点的,而且我过去已经在其他一些个人定制中使用过它。
所以,把所有这些放在一起-这些是我添加到我的~/.bashrc
中的行
# command duration puzzle
INTERACTIVE_BASHPID=$BASHPID
date +%s.%N >"/tmp/${USER}.START.${INTERACTIVE_BASHPID}"
PS0='$(date +%s.%N >"/tmp/${USER}.START.${INTERACTIVE_BASHPID}")'
PS1='$(awk "BEGIN { printf(\"%fs\", "$(date +%s.%N)" - "$(cat /tmp/$USER.START.$INTERACTIVE_BASHPID)") }" /dev/null)'"$PS1"
添加第三行( date
命令)是为了解决另一个问题。把它注释掉,开始一个新的互动狂欢,找出原因。
我用bash创建的cygwin xterm的快照,其中我将以上几行添加到了./~bashrc
:
备注:
time
命令可能提供更好的解决方案:SE: How to get execution time of a script effectively?。然而,这是一个练习bash.../tmp
目录。不时清理/tmp
或添加适当的命令进行清理(例如,添加到~/.bash_logout
).发布于 2020-06-16 19:05:38
算术扩展在当前进程中运行,并可以赋值给变量。它还生成输出,您可以使用\e[$((...,0))m
(输出\e[0m
)或${t:0:$((...,0))}
(不输出任何内容,这可能更好)来使用输出。Bash支持中的64位整数支持将把POSIX纳秒计算到2262年。
$ PS0='${t:0:$((t=$(date +%s%N),0))}'
$ PS1='$((( t )) && printf %d.%09ds $((t=$(date +%s%N)-t,t/1000000000)) $((t%1000000000)))${t:0:$((t=0))}\n$ '
0.053282161s
$ sleep 1
1.064178281s
$
$
对于空命令,不会对PS0求值,这会留下一个空行(我不确定是否可以在不破坏内容的情况下有条件地打印\n )。你可以通过切换到PROMPT_COMMAND来解决这个问题(这也节省了一个分支):
$ PS0='${t:0:$((t=$(date +%s%N),0))}'
$ PROMPT_COMMAND='(( t )) && printf %d.%09ds\\n $((t=$(date +%s%N)-t,t/1000000000)) $((t%1000000000)); t=0'
0.041584565s
$ sleep 1
1.077152833s
$
$
也就是说,如果您不需要亚秒级的精度,我建议使用$SECONDS
(如果时间设置了时间,它也更有可能返回一个合理的答案)。
https://stackoverflow.com/questions/43201274
复制