似乎通过执行PS0和PS1变量中的代码(据我所知,在运行提示命令之前和之后评估),应该可以记录每个正在运行的命令的时间并将其显示在提示中。像这样:
user@machine ~/tmp
$ sleep 1
user@machine ~/tmp 1.01s
$
Run Code Online (Sandbox Code Playgroud)
但是,由于这样的事情不起作用,我很快就陷入了PS0中的录制时间:
PS0='$(START=$(date +%s.%N))'
Run Code Online (Sandbox Code Playgroud)
据我了解,START
分配发生在子外壳中,因此在外壳中不可见。您将如何处理?
Ton*_*nyB 10
正如@tc所说PS0
,使用算术扩展允许您在and的扩展期间分配变量PS1
。较新的 bash 版本还允许PS*
样式扩展,因此您甚至不需要子 shell 即可获取当前时间。对于 bash 4.4:
# PS0 extracts a substring of length 0 from PS1; as a side-effect it causes
# the current time as epoch seconds to PS0time (no visible output in this case)
PS0='\[${PS1:$((PS0time=\D{%s}, PS1calc=1, 0)):0}\]'
# PS1 uses the same trick to calculate the time elapsed since PS0 was output.
# It also expands the previous command's exit status ($?), the current time
# and directory ($PWD rather than \w, which shortens your home directory path
# prefix to "~") on the next line, and finally the actual prompt: 'user@host> '
PS1='\nSeconds: $((PS1calc ? \D{%s}-$PS0time : 0)) Status: $?\n\D{%T} ${PWD:PS1calc=0}\n\u@\h> '
Run Code Online (Sandbox Code Playgroud)
(%N
日期指令似乎没有作为\D{...}
bash 4.4 扩展的一部分来实现。这很遗憾,因为我们只有单秒单位的分辨率。)
由于PS0
仅在有要执行的命令时才评估和打印,因此该PS1calc
标志设置为是否在扩展1
中执行时间差(在命令之后) (意味着之前未扩展,因此没有重新评估)。然后重置为. 这样,空行(只需按回车键)就不会在按下回车键之间累积秒数。PS1
PS1calc
0
PS0
PS1time
PS1
PS1calc
0
此方法的一个好处是,当您处于活动状态时没有输出set -x
。看不到任何子 shell 或临时文件:一切都在 bash 进程本身内完成。
小智 5
我正在寻找不同问题的解决方案,并遇到了这个问题,并认为这听起来很酷。除了我为其他问题开发的解决方案之外,还使用@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)
}
Run Code Online (Sandbox Code Playgroud)
bash_ 函数的输入是 bash PID
这些函数和以下内容被添加到 ~/.bashrc 文件中
ROOTPID=$BASHPID
bash_getstarttime $ROOTPID
Run Code Online (Sandbox Code Playgroud)
这些创建初始时间值并将 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'
Run Code Online (Sandbox Code Playgroud)
现在它会在处理终端输入之前在 PS0 中生成时间,处理完终端输入后在 PS1 中再次生成时间,然后计算差值并添加到 PS1。最后,这段代码会在终端退出时清理存储的时间:
function runonexit (){
rm /dev/shm/${USER}.bashtime.${ROOTPID}
}
trap runonexit EXIT
Run Code Online (Sandbox Code Playgroud)
把它们放在一起,加上一些正在测试的额外代码,它看起来像这样:
重要的部分是以毫秒为单位的执行时间,以及存储在共享内存中的所有活动终端 PID 的 user.bashtime 文件。PID 也在终端输入后立即显示,因为我将它的显示添加到 PS0,您可以看到添加和删除的 bashtime 文件。
PS0='$(bash_getstarttime $ROOTPID) $ROOTPID experiments \[\033[00m\]\n'
Run Code Online (Sandbox Code Playgroud)
我把这个当作谜题,并想展示我的谜题结果:
首先我摆弄了时间测量。(date +%s.%N
我之前没有意识到)是我的起点。不幸的是,似乎bash
s 算术评估似乎不支持浮点数。因此,我选择了其他东西:
$ START=$(date +%s.%N)
$ awk 'BEGIN { printf("%fs", '$(date +%s.%N)' - '$START') }' /dev/null
8.059526s
$
Run Code Online (Sandbox Code Playgroud)
这足以计算时间差。
接下来,我确认了您已经描述的内容:子 shell 调用会阻止使用 shell 变量。因此,我考虑在哪里可以存储开始时间,该时间对于子 shell 来说是全局的,但又足够本地化,可以同时在多个交互式 shell 中使用。我的解决方案是临时的。文件(在/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
$
Run Code Online (Sandbox Code Playgroud)
该死!我再次陷入了子外壳问题。为了解决这个问题,我定义了另一个变量:
$ 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
$
Run Code Online (Sandbox Code Playgroud)
下一步:将其与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
$
Run Code Online (Sandbox Code Playgroud)
啊。它开始起作用了。因此,只有一个问题——找到正确的启动脚本来初始化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"
Run Code Online (Sandbox Code Playgroud)
date
添加第三行(命令)是为了解决另一个问题。将其注释掉并开始一个新的交互式 bash 以找出原因。
我的 cygwin xterm 与 bash 的快照,其中我将以上行添加到./~bashrc
:
笔记:
我认为这更像是一个难题的解决方案,而不是一个“严肃有效”的解决方案。我确信这种时间测量本身会消耗大量时间。该time
命令可能提供更好的解决方案:SE:如何有效获取脚本的执行时间?。然而,这是一个很好的练习 bash 的讲座......
不要忘记,此代码会/tmp
用越来越多的小文件污染您的目录。不时清理/tmp
或添加适当的清理命令(例如 to ~/.bash_logout
)。
归档时间: |
|
查看次数: |
991 次 |
最近记录: |