【发布时间】:2020-03-02 18:26:14
【问题描述】:
我最近才开始学习shell脚本,所以不太了解。
我正在尝试查找基于时间的 while 循环但没有任何运气的示例。
我想在特定时间内运行一个循环,比如说 1 小时。所以循环运行一个小时,然后自动结束。
编辑:此循环将在没有任何睡眠的情况下连续运行,因此循环条件应基于循环的开始时间和当前时间,而不是睡眠。
【问题讨论】:
标签: shell
我最近才开始学习shell脚本,所以不太了解。
我正在尝试查找基于时间的 while 循环但没有任何运气的示例。
我想在特定时间内运行一个循环,比如说 1 小时。所以循环运行一个小时,然后自动结束。
编辑:此循环将在没有任何睡眠的情况下连续运行,因此循环条件应基于循环的开始时间和当前时间,而不是睡眠。
【问题讨论】:
标签: shell
最好的方法是使用 $SECONDS 变量,它计算脚本(或 shell)运行的时间。下面的示例展示了如何运行 3 秒的 while 循环。
#! /bin/bash
end=$((SECONDS+3))
while [ $SECONDS -lt $end ]; do
# Do what you want.
:
done
【讨论】:
#!/bin/sh 更具体的 hash-bang shell (这个答案确实如此,但我只是指出来)。
$SECONDS; @RobStarling:它确实不是 POSIX 的一部分(请参阅我对仅 POSIX 解决方案的回答);非详尽调查:$SECONDS 在bash、zsh、ksh 工作。在ksh 中,它甚至会报告小数秒。
警告:此答案中的所有解决方案(ksh 除外)都可以提前 1 秒返回(但不包括),因为它们是基于在基于实时(系统)时钟而不是基于代码执行开始时间的整数秒计数器上。
bash, ksh, zsh 解决方案,使用特殊的shell变量$SECONDS:@bsravanin 答案的略微简化版本。
简单地说,$SECONDS 包含脚本中到目前为止经过的秒数。
在bash 和zsh 中,您得到积分 秒,按系统(实时)时钟的脉冲 - 即,倒计时场景并没有真正从0(!) 开始,而是从一天中的最后一个完整时间秒开始,脚本恰好开始于或SECONDS 变量被重置。 p>
相比之下,ksh 按预期运行:当您重置$SECONDS 时,计数真正从0 开始;此外,$SECONDS 在ksh 中报告小数秒。
因此,该解决方案可合理且精确地工作的唯一 shell 是 ksh。也就是说,对于粗略测量和超时,它可能仍可用于bash 和zsh。
注意:以下使用bash shebang 行;只需将 ksh 或 zsh 替换为 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
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
【讨论】:
你可以试试这个
starttime = `date +%s`
while [ $(( $(date +%s) - 3600 )) -lt $starttime ]; do
done
'date +%s' 以秒为单位给出当前时间。
【讨论】:
您可以使用loop 命令,可用here,如下所示:
$ loop './do_thing.sh' --for-duration 1h --every 5s
它会在 1 小时内每 5 秒做一次你的事情。
【讨论】:
date +%s 将为您提供自纪元以来的秒数,例如
startTime = `date +%s`
timeSpan = #some number of seconds
endTime = timeSpan + startTime
while (( `date +%s` < endTime )) ; do
#code
done
你可能需要一些修改,因为我的 bash 已经生锈了
【讨论】:
您可以探索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
【讨论】:
这正是我想要的, 这是基于 bsravanin 回答的单行解决方案:
结束=$((SECONDS+30)); of=$((end-SECONDS)) ;而 [ $SECONDS -lt $end ];在 $of 的左边做 echo $((end-SECONDS)) 秒;睡觉 1;完成;
【讨论】:
对于更现代的方法...
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
【讨论】: