我有一个cron需要每30秒运行一次。

以下是我所拥有的:

*/30 * * * * /bin/bash -l -c 'cd /srv/last_song/releases/20120308133159 && script/rails runner -e production '\''Song.insert_latest'\'''

它能运行,但它是每30分钟或30秒运行一次吗?

此外,我一直在阅读,如果我经常运行cron,它可能不是最好的工具。有没有其他更好的工具,我可以使用或安装在Ubuntu 11.04上,这将是一个更好的选择?有没有办法修复上面的cron?


当前回答

Linux Cron基于时间的调度器默认情况下不会执行间隔小于1分钟的作业。这个配置将向您展示一个简单的技巧,如何使用Cron基于时间的调度器以秒为间隔执行作业。让我们从基础开始。以下cron作业每分钟执行一次:

* * * * * date >> /tmp/cron_test

上述作业将每分钟执行一次,并将当前时间插入到/tmp/cron_test文件中。这很简单!但是,如果我们想每30秒执行一次相同的作业呢?为此,我们使用cron来安排两个完全相同的作业,但是使用sleep命令将第二个作业的执行推迟30秒。例如:

* * * * * date >> /tmp/cron_test
* * * * * sleep 30; date >> /tmp/cron_test

其他回答

Cron的粒度以分钟为单位,并不是设计为每x秒唤醒一次以运行某项操作。在循环中运行你的重复任务,它应该做你需要的:

#!/bin/env bash
while [ true ]; do
 sleep 30
 # do what you need to here
done

使用注意:

$ watch --interval .30 script_to_run_every_30_sec.sh

Crontab job可用于以分钟/小时/天为单位调度作业,但不能以秒为单位。替代方案:

创建一个脚本,每30秒执行一次:

#!/bin/bash
# 30sec.sh

for COUNT in `seq 29` ; do
  cp /application/tmp/* /home/test
  sleep 30
done

使用crontab -e和crontab来执行这个脚本:

* * * * * /home/test/30sec.sh > /dev/null

在分钟指示符中有*/30 -这意味着每分钟,但步长为30(换句话说,每半小时)。由于cron不能精确到几分钟的分辨率,您需要找到另一种方法。

有一种可能,虽然有点拼凑(a),是有两个工作,一个抵消30秒:

# Need these to run on 30-sec boundaries, keep commands in sync.
* * * * *              /path/to/executable param1 param2
* * * * * ( sleep 30 ; /path/to/executable param1 param2 )

您将看到我添加了注释并进行了格式化,以确保它们易于保持同步。

这两个cron作业实际上每分钟运行一次,但后者在执行作业的“肉”/path/to/executable之前会等待半分钟。

关于其他(非基于cron的)选项,请参阅这里的其他答案,特别是提到fcron和systemd的答案。假设你的系统有能力使用它们(比如安装fcron或者有一个带有systemd的发行版),这些可能是更可取的。


如果您不想使用这个笨拙的解决方案,您可以使用一个基于循环的解决方案,并进行了一个小的修改。你仍然需要管理保持你的进程以某种形式运行,但是,一旦排序,下面的脚本应该工作:

#!/bin/env bash

# Debug code to start on minute boundary and to
# gradually increase maximum payload duration to
# see what happens when the payload exceeds 30 seconds.

((maxtime = 20))
while [[ "$(date +%S)" != "00" ]]; do true; done

while true; do
    # Start a background timer BEFORE the payload runs.

    sleep 30 &

    # Execute the payload, some random duration up to the limit.
    # Extra blank line if excess payload.

    ((delay = RANDOM % maxtime + 1))
    ((maxtime += 1))
    echo "$(date) Sleeping for ${delay} seconds (max ${maxtime})."
    [[ ${delay} -gt 30 ]] && echo
    sleep ${delay}

    # Wait for timer to finish before next cycle.

    wait
done

诀窍是使用sleep 30,但在有效负载运行之前在后台启动它。然后,在有效负载完成后,只需等待背景睡眠完成。

如果有效载荷需要n秒(其中n <= 30),那么有效载荷之后的等待时间将是30 - n秒。如果它需要超过30秒,那么下一个循环将被延迟,直到有效载荷完成,但不会更长。

您将看到其中的调试代码以一分钟为边界开始,以使输出最初更容易遵循。我还逐渐增加最大有效负载时间,因此您最终会看到有效负载超过30秒的周期时间(输出额外的空行,因此效果很明显)。

下面是一个示例运行(其中周期通常在前一个周期后30秒开始):

Tue May 26 20:56:00 AWST 2020 Sleeping for 9 seconds (max 21).
Tue May 26 20:56:30 AWST 2020 Sleeping for 19 seconds (max 22).
Tue May 26 20:57:00 AWST 2020 Sleeping for 9 seconds (max 23).
Tue May 26 20:57:30 AWST 2020 Sleeping for 7 seconds (max 24).
Tue May 26 20:58:00 AWST 2020 Sleeping for 2 seconds (max 25).
Tue May 26 20:58:30 AWST 2020 Sleeping for 8 seconds (max 26).
Tue May 26 20:59:00 AWST 2020 Sleeping for 20 seconds (max 27).
Tue May 26 20:59:30 AWST 2020 Sleeping for 25 seconds (max 28).
Tue May 26 21:00:00 AWST 2020 Sleeping for 5 seconds (max 29).
Tue May 26 21:00:30 AWST 2020 Sleeping for 6 seconds (max 30).
Tue May 26 21:01:00 AWST 2020 Sleeping for 27 seconds (max 31).
Tue May 26 21:01:30 AWST 2020 Sleeping for 25 seconds (max 32).
Tue May 26 21:02:00 AWST 2020 Sleeping for 15 seconds (max 33).
Tue May 26 21:02:30 AWST 2020 Sleeping for 10 seconds (max 34).
Tue May 26 21:03:00 AWST 2020 Sleeping for 5 seconds (max 35).
Tue May 26 21:03:30 AWST 2020 Sleeping for 35 seconds (max 36).

Tue May 26 21:04:05 AWST 2020 Sleeping for 2 seconds (max 37).
Tue May 26 21:04:35 AWST 2020 Sleeping for 20 seconds (max 38).
Tue May 26 21:05:05 AWST 2020 Sleeping for 22 seconds (max 39).
Tue May 26 21:05:35 AWST 2020 Sleeping for 18 seconds (max 40).
Tue May 26 21:06:05 AWST 2020 Sleeping for 33 seconds (max 41).

Tue May 26 21:06:38 AWST 2020 Sleeping for 31 seconds (max 42).

Tue May 26 21:07:09 AWST 2020 Sleeping for 6 seconds (max 43).

如果您想避免这种笨拙的解决方案,这可能更好。您仍然需要一个cron作业(或等效作业)来定期检测该脚本是否正在运行,如果没有,则启动它。但是脚本本身会处理计时。


(a)我的一些同事会说拼板是我的专长:-)

这是简单而优雅的:

* * * * * /scripts/script.sh
* * * * *  sleep 30; /scripts/script.sh

我在这里偶然发现的。