【问题标题】:How can I create a stopwatch in bash?如何在 bash 中创建秒表?
【发布时间】:2016-06-23 08:46:51
【问题描述】:

我创建了一个简单的秒表(bash 函数)来计算时间,但现在它以毫秒为单位显示当前时间。

代码:

function stopwatch() {
    date +%H:%M:%S:%N
    while true; do echo -ne "`date +%H:%M:%S:%N`\r"; done;
}

我尝试按照this answer 中的说明进行更改,但它仅适用于自 Unix 纪元以来的第二个。

当我使用 date 格式 +%s.%N 时,由于 bash 减法只需要整数,因此上述答案的减法停止工作。

我怎样才能解决它,并有一个像这样打印时间的终端秒表:

0.000000000
0.123123123
0.435345345
(and so on..)

?

【问题讨论】:

    标签: bash math time timer bash-function


    【解决方案1】:

    这是我的:

    time cat
    

    您按 Ctrl-cCtrl-d 停止并显示时间。第一个数字是时间。

    我已将其进一步细化为这个 bash 别名

    alias stopwatch="echo Press Ctrl-c to stop the timer; TIMEFORMAT=%R; time cat; unset TIMEFORMAT"
    

    【讨论】:

    • 加1。另一种可能性是使用read -n1 而不是cat。然后任何字符都会终止它。
    【解决方案2】:

    一种可以使用一天的可能(&hacky)机制:

    $ now=$(date +%s)sec
    $ while true; do
         printf "%s\r" $(TZ=UTC date --date now-$now +%H:%M:%S.%N)
         sleep 0.1
      done
    

    奖励:您可以随时按 Enter 键获得 LAP 次。 ;-)

    注意:这是一个快速修复。应该有更好的解决方案......

    基于watch 的变体(相同的逻辑):

    $ now=$(date +%s)sec; watch -n0.1 -p TZ=UTC date --date now-$now +%H:%M:%S.%N
    

    【讨论】:

    • 纳秒不会一直关闭吗?
    • 没有。纳秒开启,这甚至比我的旧解决方案更好(在另一个答案中)。伟大的! (尽管由于 CPU 使用率,我还添加了一些睡眠 :-))
    • @lewiatan 如果你正在使用睡眠,你也可以使用毫秒/微秒。
    • @123:我刚刚翻译了 OP 的代码并添加了日期减法逻辑。
    • 这会消耗相当多的资源。添加睡眠将改善这一点。我知道OP提到了毫秒。假设这是为了人类可读性(和交互),我认为像sleep 0.1s 这样的小睡眠可能是一个很好的折衷方案。
    【解决方案3】:

    如果您想要像传统秒表一样包含分钟、秒和厘秒的简单信息,您可以使用sw

    安装

    wget -q -O - http://git.io/sinister | sh -s -- -u https://raw.githubusercontent.com/coryfklein/sw/master/sw
    

    用法

    # start a stopwatch from 0, save start time in ~/.sw
    sw
    
    # resume the last run stopwatch
    sw --resume 
    

    【讨论】:

    • 致 2020 年 7 月之后查看此内容的任何人。该应用程序似乎已损坏!
    • 我刚刚在 macOS Catalina 10.15.5 上安装了它,它没有问题。随时在 GitHub 上提交问题,并提供您的详细信息@KarthikNayak。
    • 这里有一个未决问题与相同问题:github.com/coryfklein/sw/issues/2
    • 今天为我工作。
    【解决方案4】:

    这是我不久前抓到的一个更好的函数:

    function stopwatch() {
        local BEGIN=$(date +%s)
        echo Starting Stopwatch...
    
        while true; do
            local NOW=$(date +%s)
            local DIFF=$(($NOW - $BEGIN))
            local MINS=$(($DIFF / 60))
            local SECS=$(($DIFF % 60))
            local HOURS=$(($DIFF / 3600))
            local DAYS=$(($DIFF / 86400))
    
            printf "\r%3d Days, %02d:%02d:%02d" $DAYS $HOURS $MINS $SECS
            sleep 0.5
        done
    }
    

    【讨论】:

    • 你的MINS不需要修改60吗?否则您的MINS 可能会显示为 61 或 200。然后,与 HOURS 相同...需要修改 24
    【解决方案5】:

    这是对 bash 秒表的另一种看法,从该线程中的其他答案中汲取了很多内容。此版本与其他版本的不同之处包括:

    • 此版本使用 bash 算法而不是调用 bc,我发现(通过计时)可以减少 CPU 时间。
    • 我已经解决了有人指出的 25 小时限制,方法是将 24 小时添加到每一天的小时部分。 (所以现在我猜这是~31 天的限制。)
    • 我将光标留在输出的右侧,与接受答案中的版本不同。这样,您只需按 Enter 键即可轻松测量圈数(或更一般地标记重要事件时间),这会将计时器移至下一行,使按键时的时间可见。
    #!/bin/bash
    
    start_time=$(date +%s)
    
    while true; do
      current_time=$(date +%s)
      seconds_elapsed=$(( $current_time - $start_time ))
      timestamp=$(date -d"@$seconds_elapsed" -u +%-d:%-H:%-M:%-S)
    
      IFS=':' read -r day hour minute second <<< "$timestamp"
      hour="$(( $hour+24*($day-1) ))"
    
      printf "\r%02d:%02d:%02d" $hour $minute $second
      sleep 0.5
    done;
    

    这是运行 stopwatch(作为 PATH 中的可执行脚本)并在 7 秒和 18 秒时按回车键并按 Ctrl-C的示例输出> 大约 9 分钟后:

    $ stopwatch
    00:00:07
    00:00:18
    00:09:03^C
    $
    

    注意事项:

    • 我对日期使用+%-d:%-H:%-M:%-S 输出格式(这个破折号的意思是“请去掉任何前导零”),因为 printf 似乎将带有前导零的数字字符串解释为八进制并最终抱怨无效值。
    • 我摆脱了纳秒只是因为我不需要超过 1 秒的精度。因此我将sleep 的持续时间调整为更长以节省计算。

    【讨论】:

      【解决方案6】:

      对于减法,您应该使用bc (An arbitrary precision calculator language)。

      这是满足您要求的示例代码:

      function stopwatch() {
          date1=`date +%s.%N`
          while true; do
              curr_date=`date +%s.%N`
              subtr=`echo "$curr_date - $date1" | bc`
              echo -ne "$subtr\r";
              sleep 0.03
          done;
      }
      

      添加了额外的sleep 以降低 CPU 使用率(在我的机器上没有它时它几乎是 15%,而有了这个 sleep 它降低到了 1%)。

      【讨论】:

      • 您当然可以稍微改进一下:如果您直接使用:subtr=$(date "+%s.%N-$date1" | bc),则不需要curr_date 变量。您在每次迭代中保存一个子shell!您还可以将bc 置于循环之外,然后使用另一个循环将\n 输出的bc 替换为\r
      • 类似:stopwatch() { local a date1=$(date +%s.%N); while :; do date "+%s.%N-$date1"; sleep 0.03; done | bc | while read a; do printf '%s\r' "$a"; done; }。使用printf,您也可以使用其他格式,例如printf '%.3f\r' "$a"
      【解决方案7】:

      基于a gist by rawaludin

      function stopwatch() {
        local BEGIN=$(date +%s)
      
        while true; do
          local NOW=$(date +%s)
          local DIFF=$(($NOW - $BEGIN))
          local MINS=$(($DIFF / 60 % 60))
          local SECS=$(($DIFF % 60))
          local HOURS=$(($DIFF / 3600 % 24))
          local DAYS=$(($DIFF / 86400))
          local DAYS_UNIT
          [ "$DAYS" == 1 ] && DAYS_UNIT="Day" || DAYS_UNIT="Days"
      
          printf "\r  %d %s, %02d:%02d:%02d  " $DAYS $DAYS_UNIT $HOURS $MINS $SECS
          sleep 0.25
        done
      }
      

      对于对此不熟悉的人:在英语中,只有当它是1 时,我们才使用单数--Day。当是0、2、3、4、5...时,我们使用复数 "Days",所以注意是0 Days

      【讨论】:

        猜你喜欢
        • 2019-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多