每30秒执行一次cron


312

好的,我有一个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上使用或安装另一个更好的工具,这将是一个更好的选择?有没有办法解决上述问题?


CommaToast,如果您的Javascript或Java应用由于某种原因而失败并退出,该怎么办?如何重启?:-)
paxdiablo

12
添加一个小的NodeJS应用,大声笑。为什么不用一点c ++应用程序呢?在使用它时,我们可以将其命名为“ cron”并将其作为服务运行。
安德鲁


我只是在查看用户个人资料时发现此问题的,并且发现您不到1小时前就上网了(只是检查acc是否仍在使用中),是否有任何特定原因导致您不接受以下任何答案?
Fabian N.

Answers:


730

您可以*/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 30but在后台启动它。然后,在完成有效负载后,只需等待后台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工作(或同等工作)来定期检测此脚本是否正在运行,如果没有运行,请启动它。但是脚本本身然后处理时间。


(a)我的一些同事会说,鞭打是我的专长:-)


29
这是一个很好的解决方法,以至于我认为它超越了它的笨拙性
问号

14
@ rubo77,仅当运行时间少于一秒时:-)如果花费了29秒,它将发生在0:00:00、0:00.59、0:01:00、0:01:59等。
paxdiablo 2015年

1
超级偷偷摸摸,很有创意!
拉斐尔

2
第二行附近的圆括号是什么?
奈杰尔·奥尔德顿

2
这是一个很好的解决方案,对于某些需要在数分钟之内执行的任务,否则会严重降低有效性。谢谢。
Fiddy Bux

67

你不能 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'\''

这种语法是否等同于paxdiablo的语法?还是有细微的差异?
Nicolas Raoul '18

区别在于:我使用了二进制文件的原始路径。@paxdiablo使用了一种元语法。(并调用子外壳)
wildplasser

1
我的意思是,使用&&代替( ; )
Nicolas Raoul

2
抱歉。不,有区别;的&&操作者短路,因此,如果前一个失败,不执行在链中的下一个命令。
wildplasser

对于亚分钟分辨率,此粒度不应成为问题。
juan Isaza

37

Cron的粒度以分钟为单位,并非旨在每秒唤醒一次x以运行某些内容。在一个循环中运行重复的任务,它应该可以满足您的需求:

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

54
请记住,不是一样。例如,如果作业花费25秒,它将每55秒而不是每30秒开始。可能没关系,但是您应该意识到可能的后果。
paxdiablo 2012年

7
您可以在后台运行该作业,然后它将在几乎30秒内运行。
克里斯·科斯顿

1
而[true]睡觉30#做您需要做的事情---------做的应该在小情况下
圣殿堂

1
while [ true ]因为cron每分钟都会启动一个新的实例,这会不会导致您拥有许多相同脚本的实例吗?
Carcamano 2014年

2
您可以sleep $remainingTime在剩余时间为30减去工作所花费的时间上进行(如果花费的时间超过30秒,则将其限制为零)。因此,您需要花费在实际工作之前和之后的时间,并计算出差异。
mahemoff 2014年

32

如果您正在运行带有SystemD的最新Linux操作系统,则可以使用SystemD Timer单元以所需的任意粒度级别(理论上可降至纳秒级)运行脚本,并且-如果您愿意-可以比Cron允许的启动规则更灵活。无需sleep跳动

设置所需的时间比cron文件中的一行要多,但是如果您需要的内容比“每分钟”还多,那么值得付出努力。

SystemD计时器模型基本上是这样的:计时器是在计时器经过时启动服务单元的单元

因此,对于要调度的每个脚本/命令,必须有一个服务单元,然后有一个附加的计时器单元。一个计时器单元可以包含多个时间表,因此通常不需要多个计时器和一项服务。

这是一个简单的示例,每10秒记录一次“ Hello World”:

/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单元,因为它们具有相同的名称(减去单元类型后缀)。这是默认设置,但是您可以通过设置Unit[Timer]部分的字段来覆盖它。

更多血腥细节可在以下网址找到:


2
我经常在评论部分找到这样的更好答案。恕我直言,尽管cron一直是预定工作的主力军,但这个答案应该被接受,因为它不是“骇人的工作”睡眠,并且考虑到所需的时间间隔/频率,冒着并行执行长时间运行的任务的风险
Qiqo

2
精妙

21

不需要两个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'\''"


11
注意:只有在脚本运行时间不到一秒的情况下,此命令才能正确运行
rubo77

2
Rubo-如果作业需要花费数秒(而不是毫秒或微秒)的时间来完成,那么您不会每三十秒运行一次就可以每分钟运行两次。因此,是的,从30开始,然后从中减去每次运行的大约秒数(如果大于1秒)。
安德鲁

2
如果您想分别接收每次运行的错误报告,这也无济于事。
joeln

joelin-我提供的命令不会阻止获取日志或输出数据,我简化了该命令以解决该问题。为了捕获日志记录,如果需要日志记录,每个命令可以/应该重定向输出,例如,脚本/ railsrunner -e生产'\'Song.insert_latest'\'可以写为脚本/ railsrunner -e生产'\' 'Song.insert_latest'\''2>&1> / path_to_logfile,并且可以对单个cron条目中的每个命令再次执行。
安德鲁(Andrew)

13

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

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

像这样的东西

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


10

使用手表:

$ watch --interval .30 script_to_run_every_30_sec.sh

我可以使用类似的东西$ watch --interval .10 php some_file.php吗?或watch仅适用于.sh文件?
Yevhenii Shashkov

您可以使用手表运行任何东西。但是,间隔是在下--interval .30一条命令的结束与开始之间,因此不会每分钟运行两次。即watch -n 2 "sleep 1 && date +%s"它将每3秒增加一次。
jmartori

请注意,它watch是为终端使用而设计的,因此-尽管它可以在没有终端的情况下运行(nohup先注销然后运行)或在伪造的终端(例如screen)下运行-但它并不能承受类似cron的行为,例如从故障中恢复,引导后重新启动,等等。
古斯

9

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

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

$ 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 &

4
时间会漂移。例如,如果backup.sh需要1.5秒才能运行,它将每6.5秒执行一次。有多种方法可以避免这种情况,例如sleep $((5 - $(date +%s) % 5))
Keith Thompson

我是nohup的新手,在执行示例时,nohup返回“无此类文件或目录”。经过一些搜索,您似乎在nohup之后错过了“ sh”。就像这样:$ nohup的SH ./every-5-seconds.sh&
VHanded

6

使用fcron(http://fcron.free.fr/)-在几秒钟内为您提供粒度,并且比cron(vixie-cron)更好,更丰富,更稳定。我曾经做过一些愚蠢的事情,例如在一台机器上以非常愚蠢的设置运行大约60个php脚本,但它仍然能完成工作!


1
PHP开发人员的自白; )
埃里克·基加西

3
实际上,是一名系统工程师的自白使PHP开发人员可以使用.... :)
Adi Chiru

6

在目录 /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


4

目前,我正在使用以下方法。没问题。

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

如果你想运行的每个ñ秒钟,然后X将是60 / Nÿñ

谢谢。


您可能需要更改YOUR_COMMANDSYOUR_COMMANDS &,以便将命令启动到后台,否则,如果该命令花费了不到一秒钟的时间-它将延迟下次启动。因此,对于X = 2和Y = 30,如果命令花费10秒钟-它会在几分钟后启动,然后在40秒钟后而不是30秒钟启动。Kudus到@paxdiablo。
格斯,

出于某种原因,如果我忽略的/bin/bash -c部分(包括参数报价)脚本只运行每分钟,忽略了迭代(在我的情况X=12Y=5)。
abiyi

3

Crontab作业可用于以分钟/小时/天,而不是以秒为单位计划作业。替代方案:

创建一个脚本,每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

2
如果我正确理解,此脚本将运行30次,并在每次迭代之间等待30秒。在cron中每分钟运行一次有什么意义?
FuzzyAmi

2

您可以将该脚本作为服务运行,每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

1

感谢所有的好的答案。为简单起见,我喜欢使用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 

1-6代表什么?
Phantom007

@ Phantom007 1-6表示星期一至星期六,其中“-”是范围,“ 0”是星期日。这是一个很好的链接,很好地解释了所有字段以及可以在哪里进行测试的地方:“ crontab.guru/# * _ * _ * _ * _ 1-6”
jfajunior

1

编写一个Shell脚本创建.sh文件

纳米每30秒.sh

并编写脚本

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

然后为此脚本设置cron crontab -e

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

该cron调用.sh文件每1分钟运行一次,.sh文件命令在1分钟内运行2次

如果要运行脚本5秒钟,则将30替换为5并更改为如下所示的循环: For (( i=1; i <= 12; i++ ))

当您选择任何秒数时,然后计算60 /您的秒数并在For循环中写入


0

我只是要做类似的任务,并使用以下方法:

nohup watch -n30 "kill -3 NODE_PID" &

我需要每30秒进行一次周期性的杀死-3(以获取程序的堆栈跟踪信息),持续几个小时。

nohup ... & 

这是为了确保如果我松开外壳(网络问题,Windows崩溃等),我不会失去执行监视的能力。


0

看看“ 频繁cron” -它很老但是很稳定,您可以降低到微秒级。在这一点上,我唯一要反对的是,我仍在尝试找出如何在init.d之外安装它,但作为本机systemd服务,但肯定可以在Ubuntu 18之前运行仍然可以使用init.d进行操作(距离在以后的版本中可能会有所不同)。它具有附加的优势(?),确保除非之前的脚本实例已完成,否则它不会产生该PHP脚本的另一个实例,从而减少了潜在的内存泄漏问题。


-1

在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

即使假定60应该是30,你可能想以移动wget 里面if语句,否则它执行的每一秒。无论如何,我不确定这是否比single更好sleep 30。如果您正在监视实际的UNIX时间而不是您的计数器,那将会有所不同。
paxdiablo '18年

回显计数器打印出来的内容,如果未在后台运行wget,则可以找出由于EXECUTE COMMAND延迟的时间。
Lo Vega
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.