bash 脚本:固定的睡眠时间在 while 循环中累加

Max*_*axG 13 sleep shell-script

我是 bash 脚本的新手,从一些示例脚本开始。

一种是:

#!/bin/bash
SECONDS=5
i=1

while true
do
        echo "`date`: Loop $i"
        i=$(( $i+1 ))
        sleep $SECONDS
done
Run Code Online (Sandbox Code Playgroud)

这导致:

Sunday 10 May  15:08:20 AEST 2020: Loop 1
Sunday 10 May  15:08:25 AEST 2020: Loop 2
Sunday 10 May  15:08:35 AEST 2020: Loop 3
Sunday 10 May  15:08:55 AEST 2020: Loop 4
Run Code Online (Sandbox Code Playgroud)

...并且不是我期望或希望脚本做的。

为什么每次运行循环时秒数都会加倍

bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
Run Code Online (Sandbox Code Playgroud)

ImH*_*ere 26

因为SECONDS是 bash 中的内部变量。它存储外壳程序运行的时间量。

来自man bash

SECONDS
每次引用此参数时,都会返回自调用 shell 以来的秒数。如果将值分配给 SECONDS,则后续引用返回的值是自分配以来的秒数加上分配的值。

所以,你的脚本是这样工作的:

  1. 第一次使用时,它接受值 5 作为输入,初始值为 5。
  2. 然后是 5 秒的睡眠,当睡眠返回时,SECONDS 的值将是初始值的 5 秒 + 睡眠的 5 秒 = 10 秒
  3. 然后有 10 秒的睡眠(SECONDS 的当前值)加上之前 10 秒的累积时间将给出 20。
  4. 重复睡眠 20 秒加上之前的值 20 是 40。
  5. 等等。

  • 另一个规则可能是使用小写变量(秒)而不是内部定义的变量 (SECONDS)。bash 特有的小写变量很少。事实上,规则是环境变量应该是大写的。因此,在这两种情况下,请不要使用大写变量!。@MaxG (14认同)
  • (该死)...有很多东西需要学习...为什么我选择在任何变量之前使用像 Z_ 这样的字符,以确保我没有使用系统定义的变量。 (2认同)

Pau*_*ant 5

这个结构有一个二阶问题:如果循环中的处理需要大量时间(比如 1.4 秒),那么循环只会每 6.4 秒重复一次。

如果这很重要,您可以改为计算循环次数。

#!/bin/bash

Expired=$(( SECONDS + 23 ))
Tick=5
Iter=1

while (( SECONDS < Expired )); do
    echo "$(date '+%T.%N'): Loop $Iter"
    sleep 1.4  #.. Simulate work.
    echo "$(date '+%T.%N'): Idle"
    sleep $(( Iter++ * Tick - SECONDS ))
done                    
Run Code Online (Sandbox Code Playgroud)

循环时间在 SECONDS 的整数精度内是稳定的。

您可以执行更复杂的算术来初始化 Expired(例如,使用两个日期命令来查找午夜之前的秒数)。

  • `bash` 特有的另一个问题是 `SECONDS` 从 0 增加到 1 不是在 bash 启动一秒后而是在它启动后的 0 到 1 秒之间。您可能希望切换到 ksh93 或 zsh,其中修复了该问题并且支持浮点算术并具有以亚秒级粒度休眠的内置方法。请参阅[每秒精确运行一次循环](//unix.stackexchange.com/a/460969) 或[bash x 分钟的重复函数](//unix.stackexchange.com/a/411389) (3认同)