【问题标题】:Is there a way to find the running time of the last executed command in the shell?有没有办法在shell中找到最后执行的命令的运行时间?
【发布时间】:2010-04-24 13:37:11
【问题描述】:

有没有像time这样的命令可以显示shell上最后或过去执行的命令的运行时间详情?

【问题讨论】:

    标签: linux bash macos unix shell


    【解决方案1】:

    zsh 有一些内置功能可以计算命令需要多长时间。

    如果您启用了 inc_append_history_time 选项

    setopt inc_append_history_time

    然后运行每个命令所花费的时间会保存在您的历史记录中,然后可以使用history -D查看。

    【讨论】:

      【解决方案2】:

      我不知道,它在 bash 中如何,但在 zsh 中你可以定义 preexecprecmd 函数,以便它们将当前时间保存到变量 $STARTTIME (preexec) 和 $ENDTIME (precmd)因此您将能够找到大致的运行时间。或者您可以定义一个accept-line 函数,以便在每个命令之前添加time

      更新: 这是代码,它将在$_elapsed 数组中存储经过的时间:

      preexec () {
         (( $#_elapsed > 1000 )) && set -A _elapsed $_elapsed[-1000,-1]
         typeset -ig _start=SECONDS
      }
      precmd() { set -A _elapsed $_elapsed $(( SECONDS-_start )) }
      

      那么如果你运行sleep 10s:

      % set -A _elapsed # Clear the times
      % sleep 10s
      % sleep 2s ; echo $_elapsed[-1]
      10
      % echo $_elapsed
      0 10 0
      

      不需要四个变量。名称或额外延迟没有问题。请注意,$_elapsed 数组可能会变得非常大,因此您需要删除第一个项目(这是通过以下代码完成的:(( $#_elapsed > 1000 )) && set -A _elapsed $_elapsed[-1000,-1])。

      更新2: 在 bash 中找到了支持 zsh 样式的 precmd 和 preexec 的脚本。也许您需要删除typeset -ig(我曾经只是强制$_start 为整数)并将set -A var ... 替换为var=( ... ) 以使其正常工作。而且我不知道如何在 bash 中对数组进行切片并获取它们的长度。

      脚本:http://www.twistedmatrix.com/users/glyph/preexec.bash.txt

      更新3: 发现一个问题:如果你用空行点击返回,preexec 不会运行,而 precmd 会运行,所以你将在 $_elapsed 数组中得到无意义的值。为了解决这个问题,请使用以下代码替换 precmd 函数:

      precmd () {
         (( _start >= 0 )) && set -A _elapsed $_elapsed $(( SECONDS-_start ))
         _start=-1 
      }
      

      【讨论】:

      • 这种方法的问题类似于我在回答中提到的问题。例如,如果程序 A 启动并完成,经过一段时间,程序 B 启动并完成,则时间差包括该延迟。
      • 也许您可以将您的脚本与EXTENDED_HISTORY 选项结合起来以提高健壮性?只是扔一个adea。我没有超过 10s 的思考时间,我的睡眠时间已经结束了(等待脚本完成它的工作)。
      【解决方案3】:

      编辑 3:

      这个答案的结构:

      1. 没有现成的方法来为已经运行的命令计时
      2. 您可以通过多种方式推断命令运行时间的持续时间。
      3. 显示了概念证明(从无法完成的假设开始,到假设错误的结论结束)。
      4. 您可以预先设置一些技巧来记录您运行的每个命令的运行时间
      5. 结论

      根据上面的大纲按其部分标记的答案:

      第 1 部分 - 简短的回答是“不”

      原创

      不,对不起。你必须使用time

      第 2 部分 - 也许你可以推断出结果

      在某些情况下,如果程序在日志文件中写入输出文件或信息,您可能能够推断出运行时间,但这将是特定于程序的并且只是一种猜测。如果您在 Bash 中设置了 HISTTIMEFORMAT,则可以查看历史文件中的条目以了解程序何时启动。但是没有记录结束时间,因此您只能在您感兴趣的节目结束后立即启动另一个节目时推断持续时间。

      第 3 部分 - 假设被证伪

      假设:空闲时间将计入经过的时间

      编辑:

      这里有一个例子来说明我的观点。它基于 ZyX 的建议,但使用其他方法会类似。

      zsh:

      % precmd() { prevstart=start; start=$SECONDS; }
      % preexec() { prevend=$end; end=$SECONDS; }
      % echo "T: $SECONDS ps: $prevstart pe: $prevend s: $start e: $end"
      T: 1491 ps: 1456 pe: 1458 s: 1459 e: 1491
      

      现在我们等待...假设等待 15 秒,然后:

      % echo "T: $SECONDS"; sleep 10
      T: 1506
      

      现在我们等待...假设 20 秒,然后:

      % echo "T: $SECONDS ps: $prevstart pe: $prevend s: $start e: $end"
      T: 1536 ps: 1492 pe: 1506 s: 1516 e: 1536
      

      如您所见,我错了。开始时间 (1516) 减去 上一个 结束时间 (1506) 命令的持续时间 (sleep 10)。 这也说明我在函数中使用的变量需要更好的名字。

      假设不成立 - 有可能在不包括空闲时间的情况下获得正确的经过时间

      第 4 部分 - 记录每个命令所用时间的技巧

      编辑 2:

      这是 ZyX 的 答案中函数的 Bash 等效项(它们需要链接到那里的脚本):

      preexec () {
         (( ${#_elapsed[@]} > 1000 )) && _elapsed=(${_elapsed[@]: -1000})
         _start=$SECONDS
      }
      
      precmd () {
         (( _start >= 0 )) && _elapsed+=($(( SECONDS-_start )))
         _start=-1 
      }
      

      安装preexec.bash(来自链接脚本)并创建上面的两个函数后,示例运行如下所示:

      $ _elapsed=() # Clear the times
      $ sleep 10s
      $ sleep 2s ; echo ${_elapsed[@]: -1}
      10
      $ echo ${_elapsed[@]}
      0 10 2
      

      第 5 部分 - 结论

      使用time

      【讨论】:

      • 我不明白你的问题。我的示例函数工作得很好。查看更新的答案。
      • @ZyX:我的假设是这不能在 Bash(或 zsh)中完成。正如我在包含测试函数的编辑中所说,我的假设是错误的。而且您的功能(在我编辑答案时尚未启动)显然比我的测试功能更好,是的,它们确实有效。
      • 我对你的回答有点困惑,但不敢编辑它(因为我很困惑):这可能吗?您能否澄清一下并删除(或删除)不正确的地方?因为你写了两个:“不,你不能”然后“我错了”,但很难不混淆:)
      • 非常感谢。我支持你,但我猜 ZyX 应该得到答案。
      【解决方案4】:

      我认为您正在运行需要很长时间的命令,并且在开始时没有意识到您想要计算它们需要多长时间才能完成。在 zsh 中,如果您将环境变量 REPORTTIME 设置为一个数字,则任何花费超过该秒数的命令都将打印它所花费的时间,就好像您已经在它前面运行了 time 命令一样。你可以在你的 .zshrc 中设置它,这样长时间运行的命令总是会打印它们的时间。请注意,睡眠时间(相对于用户/系统时间)不计入触发计时器,但仍会被跟踪。

      【讨论】:

        【解决方案5】:

        我认为您只能获取使用命令“时间”运行的命令的计时统计信息。

        来自手册页:

        时间 [选项] 命令 [参数...]

        描述 time 命令使用给定的参数运行指定的程序命令。当命令完成时, time 向标准错误写入一条消息,提供有关此程序运行的计时统计信息。

        【讨论】:

          【解决方案6】:

          我编写了一个工具(discalimer!),它通过劫持到正在运行的 shell 进程(目前只有 支持 bash)。与此处发布的其他解决方案相比,它还正确跟踪异步命令的运行时间,例如sleep 5 &.
          在这里查看:

          https://github.com/tycho-kirchner/shournal

          【讨论】:

            【解决方案7】:

            可以使用history 命令与纪元秒%s 和内置变量$EPOCHSECONDS 来计算命令何时完成,仅利用$PROMPT_COMMAND

            # Save start time before executing command (does not work due to PS0 sub-shell)
            # preexec() {
            #   STARTTIME=$EPOCHSECONDS
            # }
            # PS0=preexec
            
            # Save end time, without duplicating commands when pressing Enter on an empty line
            precmd() {
                local st=$(HISTTIMEFORMAT='%s ' history 1 | awk '{print $2}');
                if [[ -z "$STARTTIME" || (-n "$STARTTIME" && "$STARTTIME" -ne "$st") ]]; then
                    ENDTIME=$EPOCHSECONDS
                    STARTTIME=$st
                else
                    ENDTIME=0
                fi
            }
            
            __timeit() {
                precmd;
                if ((ENDTIME - STARTTIME >= 0)); then
                    printf 'Command took %d seconds.\n' "$((ENDTIME - STARTTIME))";
                fi
            
                # Do not forget your:
                #     - OSC 0 (set title)
                #     - OSC 777 (notification in gnome-terminal, urxvt; note, this one has preexec and precmd as OSC 777 features)
                #     - OSC 99 (notification in kitty)
                #     - OSC 7 (set url) - out of scope for this question
            }
            
            export PROMPT_COMMAND=__timeit
            

            注意:如果您的 $HISTCONTROL 中有 ignoredups,则不会报告重新运行的命令。

            参考(抄自我自己对类似问题的回答):

            Use PS0 and PS1 to display execution time of each bash command

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2022-11-23
              • 1970-01-01
              • 1970-01-01
              • 2021-10-20
              • 2013-10-06
              • 1970-01-01
              • 2014-03-31
              • 1970-01-01
              相关资源
              最近更新 更多