每30秒运行一次cron

我有一个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?

828664 次浏览

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

一种可能,尽管它有点拼凑__abc0,是有两个工作,一个抵消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

对于其他(非基于- __abc0的)选项,请参阅这里的其他答案,特别是提到fcronsystemd的答案。假设你的系统有能力使用它们(比如安装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,但在有效负载运行之前在背景中启动它。然后,在有效负载完成后,只需等待后台sleep完成。

如果有效载荷需要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作业(或等效的)来定期检测这个脚本是否正在运行,如果没有,就启动它。但是脚本本身会处理计时。


我的一些同事会说kludges是我的专长:-)

你不能。Cron的粒度为60秒。

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

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

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

你可以看看我对类似的问题的回答

基本上,我在这里包含了一个名为“runEvery.sh”的bash脚本,您可以用cron每1分钟运行一次该脚本,并将希望运行的实际命令和希望运行该命令的频率(以秒为单位)作为参数传递。

就像这样

* * * * * ~/bin/runEvery.sh 5 myScript.sh

Cron作业不能用于以秒为间隔调度作业。例如,你不能安排一个cron作业每5秒运行一次。另一种方法是编写一个shell脚本,在其中使用sleep 5命令。

使用bash while循环每5秒创建一个shell脚本。sh,如下所示。

$ cat every-5-seconds.sh
#!/bin/bash
while true
do
/home/ramesh/backup.sh
sleep 5
done

现在,在后台使用nohup执行这个shell脚本,如下所示。这将在退出会话后继续执行脚本。这将每5秒执行一次backup.sh shell脚本。

$ nohup ./every-5-seconds.sh &

使用fcron (http://fcron.free.fr/) -以秒为单位提供粒度,比cron (vixie-cron)更好,功能更丰富,也更稳定。我曾经做过一些愚蠢的事情,比如在一台机器上运行60个php脚本,设置非常愚蠢,但它仍然发挥了作用!

不需要两个cron条目,你可以把它放在一个:

* * * * * /bin/bash -l -c "/path/to/executable; sleep 30 ; /path/to/executable"

在你的例子中:

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

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

使用注意:

$ watch --interval .30 script_to_run_every_30_sec.sh

我刚刚有一个类似的任务要做,并使用了以下方法:

nohup watch -n30 "kill -3 NODE_PID" &

我需要在几个小时内每30秒执行一次定期kill -3(以获得程序的堆栈跟踪)。

nohup ... &

这是在这里确保我不会失去手表的执行,如果我松散的外壳(网络问题,windows崩溃等…)

在dir /etc/cron.d/

新建一个文件excute_per_30s

* * * * * yourusername  /bin/date >> /home/yourusername/temp/date.txt
* * * * * yourusername sleep 30; /bin/date >> /home/yourusername/temp/date.txt

是否每30秒运行一次cron

谢谢你的回答。简单来说,我喜欢混合的解决方案,crontab上的控件和脚本上的时间划分。这就是我所做的每20秒运行一个脚本(每分钟3次)。Crontab行:

 * * * * 1-6 ./a/b/checkAgendaScript >> /home/a/b/cronlogs/checkAgenda.log

脚本:

cd /home/a/b/checkAgenda


java -jar checkAgenda.jar
sleep 20
java -jar checkAgenda.jar
sleep 20
java -jar checkAgenda.jar

在shell循环中运行,示例:

#!/bin/sh
counter=1
while true ; do
echo $counter
counter=$((counter+1))
if [[ "$counter" -eq 60 ]]; then
counter=0
fi
wget -q http://localhost/tool/heartbeat/ -O - > /dev/null 2>&1 &
sleep 1
done

写一个shell脚本 创建

.sh文件

纳米every30second.sh

并编写脚本

#!/bin/bash
For  (( i=1; i <= 2; i++ ))
do
write Command here
sleep 30
done

然后为这个脚本设置cron crontab - e < / >强

(* * * * * /home/username/every30second.sh)

这个cron每1分钟调用一次.sh文件。在“。sh”文件中,1分钟内执行2次

如果你想运行脚本5秒,那么将30替换为5,并像这样更改for循环:For (( i=1; i <= 12; i++ ))

当你选择任意一秒时,计算60/你的秒并写入for循环

目前我正在使用下面的方法。工作没有问题。

* * * * * /bin/bash -c ' for i in {1..X}; do YOUR_COMMANDS ; sleep Y ; done '

如果你想每N秒运行一次,那么X将是60 / N,而Y将是N

如果您正在运行带有SystemD的最新Linux操作系统,您可以使用SystemD Timer单元以您希望的任何粒度级别运行脚本(理论上可以精确到纳秒),并且—如果您愿意—比Cron所允许的更灵活的启动规则。不需要sleep插件

在cron文件中只设置一行需要更多的时间,但是如果您需要的不是“每一分钟”,那么这样做是非常值得的。

SystemD定时器模型基本上是这样的:计时器是在计时器失效时启动服务单元的单位

因此,对于想要调度的每个脚本/命令,必须有一个服务单元和一个额外的计时器单元。单个计时器单元可以包括多个计划,因此通常不需要多个计时器和一个服务。

下面是一个简单的例子,记录“Hello world”;每10秒:

/etc/systemd/system/helloworld.service:

[Unit]
Description=Say Hello
[Service]
ExecStart=/usr/bin/logger -i Hello World

/etc/systemd/system/helloworld.timer:

[Unit]
Description=Say Hello every 10 seconds
[Timer]
OnBootSec=10
OnUnitActiveSec=10
AccuracySec=1ms
[Install]
WantedBy=timers.target

在设置这些单元之后(在/etc/systemd/system中,如上所述,用于系统范围的设置,或在~/.config/systemd/user中,用于特定于用户的设置),你需要通过运行systemctl enable --now helloworld.timer来启用计时器(而不是服务)(--now标志也会立即启动计时器,否则,它只会在下一次引导或用户登录后启动)。

这里使用的[Timer]节字段如下:

  • OnBootSec -在每次引导之后启动服务。
  • OnUnitActiveSec -在服务最后一次启动后这么多秒启动服务。这就是导致计时器重复自己并表现得像cron作业的原因。
  • AccuracySec -设置计时器的精度。计时器的精确程度取决于该字段的设置,默认值是1分钟(模拟cron)。不要求最佳精度的主要原因是为了提高功耗——如果SystemD可以安排下一次运行与其他事件相一致,它就需要减少唤醒CPU的频率。上面例子中的1ms并不理想——在我的次分钟计划作业中,我通常将精度设置为1(1秒),但这意味着如果你查看显示“Hello world”的日志;消息,你会看到它通常晚1秒。如果你觉得没问题,我建议你将精度设置为1秒或更长时间。

正如您可能已经注意到的那样,这个计时器并没有很好地模拟Cron -在某种意义上,该命令并不是在每个挂钟周期的开始时开始的(即它不是在时钟上的第10秒开始,然后是第20秒等等)。相反,它只发生在计时器流逝的时候。如果系统在12:05:37启动,那么下一次运行命令的时间将是12:05:47,然后是12:05:57,以此类推。如果你对实际的挂钟精度感兴趣,那么你可能想要替换OnBootSecOnUnitActiveSec字段,而用你想要的时间表设置OnCalendar规则(据我所知,使用日历格式,不能超过1秒)。上面的例子也可以写成:

OnCalendar=*-*-* *:*:00,10,20,30,40,50

最后注意:正如你可能猜到的那样,helloworld.timer单元开始helloworld.service单元,因为它们有相同的名称(减去单元类型后缀)。这是默认值,但是你可以通过为[Timer]节设置Unit字段来覆盖它。

更多血腥的细节可以在这里找到:

看看frequent-cron——它很老,但非常稳定,你可以把它降级到微秒级。在这个时间点上,我唯一要说的反对它的是,我仍然在试图解决如何在init之外安装它。但是作为一个原生systemd服务,但是在Ubuntu 18之前,它仍然使用init运行得很好。D(距离可能在后面的版本有所不同)。它还有一个额外的优点(?),即确保它不会生成另一个PHP脚本实例,除非前一个实例已经完成,这减少了潜在的内存泄漏问题。

你可以将脚本作为服务运行,每30秒重新启动一次

注册服务

sudo vim /etc/systemd/system/YOUR_SERVICE_NAME.service

粘贴下面的命令

Description=GIVE_YOUR_SERVICE_A_DESCRIPTION


Wants=network.target
After=syslog.target network-online.target


[Service]
Type=simple
ExecStart=YOUR_COMMAND_HERE
Restart=always
RestartSec=10
KillMode=process


[Install]
WantedBy=multi-user.target

重新加载服务

sudo systemctl daemon-reload

启用服务

sudo systemctl enable YOUR_SERVICE_NAME

启动服务

sudo systemctl start YOUR_SERVICE_NAME

检查您的服务状态

systemctl status YOUR_SERVICE_NAME
经过反复试验,我找到了正确的表达式:*/30 * * ?* * * 换算成每30秒。 参考:https://www.freeformatter.com/cron-expression-generator-quartz.html 他们提供了每秒运行的表达式:* * * ?* * * */x用于每x个单位运行一次。我在分位和中提琴上试过了。我相信其他人已经发现了这一点,但我想分享我的顿悟时刻!: D < / p >

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