每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,依此类推。如果您对实际的挂钟精度感兴趣,则可以想要替换OnBootSec和OnUnitActiveSec字段,而是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_COMMANDS为YOUR_COMMANDS &,以便将命令启动到后台,否则,如果该命令花费了不到一秒钟的时间-它将延迟下次启动。因此,对于X = 2和Y = 30,如果命令花费10秒钟-它会在几分钟后启动,然后在40秒钟后而不是30秒钟启动。Kudus到@paxdiablo。
— 格斯,

出于某种原因,如果我忽略的/bin/bash -c部分(包括参数报价)脚本只运行每分钟,忽略了迭代(在我的情况X=12和Y=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.