【问题标题】:Time condition loop in shellshell中的时间条件循环
【发布时间】:2020-03-02 18:26:14
【问题描述】:

我最近才开始学习shell脚本,所以不太了解。

我正在尝试查找基于时间的 while 循环但没有任何运气的示例。

我想在特定时间内运行一个循环,比如说 1 小时。所以循环运行一个小时,然后自动结束。

编辑:此循环将在没有任何睡眠的情况下连续运行,因此循环条件应基于循环的开始时间和当前时间,而不是睡眠。

【问题讨论】:

    标签: shell


    【解决方案1】:

    最好的方法是使用 $SECONDS 变量,它计算脚本(或 shell)运行的时间。下面的示例展示了如何运行 3 秒的 while 循环。

    #! /bin/bash
    end=$((SECONDS+3))
    
    while [ $SECONDS -lt $end ]; do
        # Do what you want.
        :
    done
    

    【讨论】:

    • 这受到 bash 和可能的其他 bourne shell 变体的支持,但我不确定它是否在 POSIX 规范中,所以你应该清楚地指定一个比 #!/bin/sh 更具体的 hash-bang shell (这个答案确实如此,但我只是指出来)。
    • +1 用于$SECONDS; @RobStarling:它确实不是 POSIX 的一部分(请参阅我对仅 POSIX 解决方案的回答);非详尽调查:$SECONDSbashzshksh 工作。在ksh 中,它甚至会报告小数秒。
    【解决方案2】:

    警告:此答案中的所有解决方案(ksh 除外)都可以提前 1 秒返回(但不包括),因为它们是基于在基于实时(系统)时钟而不是基于代码执行开始时间的整数秒计数器上。


    bash, ksh, zsh 解决方案,使用特殊的shell变量$SECONDS:

    @bsravanin 答案的略微简化版本。

    简单地说,$SECONDS 包含脚本中到目前为止经过的秒数。

    bashzsh 中,您得到积分 秒,按系统(实时)时钟的脉冲 - 即,倒计时场景并没有真正从0(!) 开始,而是从一天中的最后一个完整时间秒开始,脚本恰好开始于或SECONDS 变量被重置。 p>

    相比之下,ksh 按预期运行:当您重置$SECONDS 时,计数真正从0 开始;此外,$SECONDSksh 中报告小数秒。

    因此,该解决方案可合理且精确地工作的唯一 shell 是 ksh。也就是说,对于粗略测量和超时,它可能仍可用于bashzsh

    注意:以下使用bash shebang 行;只需将 kshzsh 替换为 bash 即可使脚本也与这些 shell 一起运行。

    #!/usr/bin/env bash
    
    secs=3600   # Set interval (duration) in seconds.
    
    SECONDS=0   # Reset $SECONDS; counting of seconds will (re)start from 0(-ish).
    while (( SECONDS < secs )); do    # Loop until interval has elapsed.
      # ...
    done
    

    针对仅具有 POSIX 功能的 shell 的解决方案,例如 Ubuntu 上的 sh (dash)($SECONDS 不符合 POSIX)

    @dcpmero 答案的清理版。

    使用date +%s 返回的纪元时间(自 1970 年 1 月 1 日以来经过的秒数)和条件的 POSIX 语法。

    警告date +%s 本身(特别是 %s 格式)符合 POSIX,但它(至少)可以在 Linux 上运行, FreeBSD 和 OSX。

    #!/bin/sh
    
    secs=3600                         # Set interval (duration) in seconds.
    endTime=$(( $(date +%s) + secs )) # Calculate end time.
    
    while [ $(date +%s) -lt $endTime ]; do  # Loop until interval has elapsed.
        # ...
    done
    

    【讨论】:

      【解决方案3】:

      你可以试试这个

      starttime = `date +%s`
      while [ $(( $(date +%s) - 3600 )) -lt $starttime ]; do
      done
      

      'date +%s' 以秒为单位给出当前时间。

      【讨论】:

        【解决方案4】:

        您可以使用loop 命令,可用here,如下所示:

        $ loop './do_thing.sh' --for-duration 1h --every 5s
        

        它会在 1 小时内每 5 秒做一次你的事情。

        【讨论】:

        • 真的很好,但不是标准的:/
        【解决方案5】:

        date +%s 将为您提供自纪元以来的秒数,例如

        startTime = `date +%s`
        timeSpan = #some number of seconds
        endTime = timeSpan + startTime
        
        while (( `date +%s` < endTime )) ; do
            #code
        done
        

        你可能需要一些修改,因为我的 bash 已经生锈了

        【讨论】:

        • 很有希望,但您的代码由于多种原因而中断;看我的回答。
        【解决方案6】:

        您可以探索date-d 选项。

        下面是一个shell脚本sn-p来举例说明。它与其他答案类似,但在不同的场景中可能更有用。

        # set -e to exit if the time provided by argument 1 is not valid for date.
        # The variable stop_date will store the seconds since 1970-01-01 00:00:00
        # UTC, according to the date specified by -d "$1".
        set -e
        stop_date=$(date -d "$1" "+%s")
        set +e
        
        echo -e "Starting at $(date)"
        echo -e "Finishing at $(date -d "$1")"
        
        # Repeat the loop while the current date is less than stop_date
        while [ $(date "+%s") -lt ${stop_date} ]; do
        
            # your commands that will run until stop_date
        
        done
        

        然后,您可以使用 date 理解的多种不同方式调用脚本:

        $ ./the_script.sh "1 hour 4 minutes 3 seconds"
        Starting at Fri Jun  2 10:50:28 BRT 2017
        Finishing at Fri Jun  2 11:54:31 BRT 2017
        
        $ ./the_script.sh "tomorrow 8:00am"
        Starting at Fri Jun  2 10:50:39 BRT 2017
        Finishing at Sat Jun  3 08:00:00 BRT 2017
        
        $ ./the_script.sh "monday 8:00am"
        Starting at Fri Jun  2 10:51:25 BRT 2017
        Finishing at Mon Jun  5 08:00:00 BRT 2017
        

        【讨论】:

          【解决方案7】:

          这正是我想要的, 这是基于 bsravanin 回答的单行解决方案:

          结束=$((SECONDS+30)); of=$((end-SECONDS)) ;而 [ $SECONDS -lt $end ];在 $of 的左边做 echo $((end-SECONDS)) 秒;睡觉 1;完成;

          【讨论】:

            【解决方案8】:

            对于更现代的方法...

            重击

            declare -ir MAX_SECONDS=5
            declare -ir TIMEOUT=$SECONDS+$MAX_SECONDS
            
            while (( $SECONDS < $TIMEOUT )); do
                # foo
            done
            

            科恩

            typeset -ir MAX_SECONDS=5
            typeset -ir TIMEOUT=$SECONDS+$MAX_SECONDS
            
            while (( $SECONDS < $TIMEOUT )); do
                # bar
            done
            

            【讨论】:

              猜你喜欢
              • 2018-09-09
              • 2019-12-10
              • 2021-12-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-03-05
              相关资源
              最近更新 更多