PS1提示显示已用时间

PS1提示显示已用时间

我目前使用它在 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 ~>

这与是否可以通过后台脚本定期更改 PS1?

答案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,但最终值为$x15。

这很大程度上$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;7m7请求带有绿色 (“ ” ) 背景的反视频 ( “ 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'

相关内容