53

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

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

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

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

4

8 回答 8

107

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

#! /bin/bash
end=$((SECONDS+3))

while [ $SECONDS -lt $end ]; do
    # Do what you want.
    :
done
于 2012-06-25T23:12:59.213 回答
31

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


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

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

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

并且您会通过系统(实时)时钟bash的脉冲获得积分秒数- 即,幕后计数并不是真正从(!)开始,而是从一天中最后一个完整时间秒以来的任何分数脚本恰好开始于或变量被重置。zsh0SECONDS

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

因此,该解决方案可以合理且准确地在其中工作的唯一外壳是ksh. 也就是说,对于粗略的测量和超时,它仍然可以在bashzsh中使用。

注:以下使用bashshebang线;简单地替换kshzshforbash将使脚本也与这些 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( )(不符合 POSIX 标准)dash$SECONDS

@dcomero 的答案的清理版本。

使用返回的纪元时间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
于 2014-03-29T19:23:13.283 回答
2

You can try this

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

where 'date +%s' gives the current time in seconds.

于 2015-01-16T06:31:04.013 回答
1

date +%s会给你自纪元以来的秒数,所以像

startTime = `date +%s`
timeSpan = #some number of seconds
endTime = timeSpan + startTime

while (( `date +%s` < endTime )) ; do
    #code
done

你可能需要一些编辑,因为我的 bash 生锈了

于 2012-06-25T22:51:24.667 回答
1

这正是我一直在寻找的,这是基于 bsravanin 回答的单行解决方案:

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

于 2018-09-13T10:58:54.510 回答
1

您可以-d探索date.

下面是一个示例的 shell 脚本片段。它类似于其他答案,但在不同的场景中可能更有用。

# 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
于 2017-06-02T15:45:29.997 回答
1

您可以使用此处loop提供的命令,如下所示:

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

它将每五秒钟做一次你的事情,持续一小时。

于 2018-06-26T20:38:45.853 回答
0

对于更现代的方法...

重击

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
于 2020-03-02T10:26:14.203 回答