HOWTO · Bash

如何让 Bash 脚本暂停指定秒数

了解如何在 Bash 中使用 sleep 和 read,让脚本按时延迟或等待输入。

本页内容

Bash 脚本可以暂停一段时间,让读者有机会看完提示、等待交互输入,或在命令之间加入延迟。需要按秒延迟时使用 sleep;需要等待输入时使用 read。这两种暂停控制的是脚本何时继续执行,单纯延迟并不能协调多个脚本对共享文件的访问。

暂停 Bash 脚本 X 秒

使用 sleep 和以秒为单位的整数,可以延迟执行下一条命令。时长参数是必需的。sleep 手册介绍了该命令;是否支持小数秒或时长后缀,取决于系统安装的 sleep 实现。

下面是一个完整的 Bash 脚本:它先打印一条消息,请求等待一秒,然后再打印另一条消息:

#!/usr/bin/env bash
printf '%s\n' 'The script is starting.'
sleep 1
printf '%s\n' 'One second wait requested.'

请求的等待时长只是目标,并不保证实际经过的时间恰好相同;系统调度可能让等待更久。若要在暂停一整秒后重复执行某项操作,可以把该操作和 sleep 放进循环中。让循环只执行目标操作,这样每次重复都会有明确的效果。

暂停 Bash 脚本一段可变时长

可以把以整秒计的时长存入变量,并在传给 sleep 时引用变量展开结果。下面的脚本默认等待五秒;也可以在环境变量中设置 N 来选择其他值:

#!/usr/bin/env bash
N=${N:-5}
sleep "$N"
printf 'Requested a %s-second wait.\n' "$N"

例如,运行 N=8 ./your-script.sh 可以为这一次调用设置时长。该值必须是当前 sleep 命令接受的有效时长。加引号可以避免分词和路径名展开,但不能让任意输入自动成为有效时长,因此应先验证来自用户或其他不可信来源的值。

在 Bash 中,可以用 $RANDOM 将随机延迟限制在一个范围内。下面的脚本请求等待一到三秒:

#!/usr/bin/env bash
delay=$((1 + RANDOM % 3))
printf 'Requested a %s-second wait.\n' "$delay"
sleep "$delay"

随机延迟可以让不同脚本运行的开始时间有所变化,但不能确保两个运行实例不会同时写入同一个文件。脚本需要共享资源时,应使用适当的锁或协调机制。

暂停 Bash 脚本,直到收到用户输入

Bash 的 read 命令会等待标准输入中的一行内容。在交互式终端中,用户通常按 Enter 键结束这一行。使用 -r 可让输入中的反斜杠保持原样,使用 -p 可显示提示信息:

#!/usr/bin/env bash
if read -r -p 'Press Enter to continue: ' reply; then
    printf '%s\n' 'Continuing.'
else
    printf '%s\n' 'No input line was available.'
fi

这种写法预期会收到交互式输入,也可能一直等待下去。如果标准输入到达文件末尾或被重定向,read 可能在未收到输入行时返回;检查它的状态,就能处理这种情况。Bash read 参考手册说明了它的选项和行为。

将定时暂停与等待用户输入结合起来

如果希望用户输入可以提前结束等待,同时也希望超时后脚本继续执行,可以使用 Bash 的 read -t。超时时长以秒为单位,系统调度也可能使实际等待更久。下面的示例会说明是哪种情况让脚本继续执行:

#!/usr/bin/env bash
if read -r -t 5 -p 'Continue now, or wait for the timeout: ' reply; then
    printf '%s\n' 'Input received; continuing.'
else
    printf '%s\n' 'No line was read before the wait ended; continuing.'
fi

若要让用户选择整秒时长,可以先把输入读入变量,再将加引号的变量值传给 sleep。下面的写法会在调用 sleep 前检查输入是否为不超过三位的数字;如果没有合适的输入行,也会给出提示:

#!/usr/bin/env bash
if read -r -p 'Enter a whole number of seconds (up to 3 digits): ' duration \
    && [[ $duration =~ ^[0-9]{1,3}$ ]]; then
    sleep "$duration"
    printf 'Requested a %s-second wait.\n' "$duration"
else
    printf '%s\n' 'Enter a whole number with at most 3 digits.'
fi

在输入传给 sleep 前先进行检查,可以避免把格式错误的文本当作时长传入。若脚本需要不同的限制,可以调整允许的范围,并向使用者说明这些限制。

简而言之,需要按整秒计时延迟时使用 sleep,需要等待输入时使用 read。Bash 的 read -t 将两者结合起来:输入行到达或超时后,等待就会结束。