每三天执行一次Bash脚本


8

我想每3天执行一次Shell脚本。结合使用crontab并01 00 */3 * *不能真正满足条件,因为它将在每月31日运行,然后在一个月的第一天再次运行。该*/3语法是相同的话说,1,4,7 ...... 25,28,31。

应该有办法使脚本本身检查条件,如果还没有3天,则退出。因此crontab每天都会执行该脚本,但是脚本本身会检查是否已经过去了3天。

我什至找到了一些代码,但是它给了我语法错误,任何帮助将不胜感激。

if (! (date("z") % 3)) {
     exit;
}

main.sh: line 1: syntax error near unexpected token `"z"'
main.sh: line 1: `if (! (date("z") % 3)) {'

4
到底是什么意思 怎么*/3不行?“如果三天还没有过去”:三天后呢?请编辑您的问题并进行澄清。
terdon

4
从字面上看?这似乎是一个x / y问题,您可能想要实际谈论您要做什么。您是否要阻止crontab运行脚本?
Journeyman Geek

1
编辑了问题,以解释为什么crontab解决方案不起作用。
塔维

喜欢的东西date("z") % 3 == 0会从类似问题的困扰:该条件将是12月29日和1月3日之间四天假,除非该月是闰年的一部分。
Rhymoid

Answers:


12

要在最后一次执行尚未至少在特定时间之前立即中止并退出脚本,可以使用此方法,该方法需要一个存储最后执行日期和时间的外部文件。

将这些行添加到您的Bash脚本的顶部:

#!/bin/bash

# File that stores the last execution date in plain text:
datefile=/path/to/your/datefile

# Minimum delay between two script executions, in seconds. 
seconds=$((60*60*24*3))

# Test if datefile exists and compare the difference between the stored date 
# and now with the given minimum delay in seconds. 
# Exit with error code 1 if the minimum delay is not exceeded yet.
if test -f "$datefile" ; then
    if test "$(($(date "+%s")-$(date -f "$datefile" "+%s")))" -lt "$seconds" ; then
        echo "This script may not yet be started again."
        exit 1
    fi
fi

# Store the current date and time in datefile
date -R > "$datefile"

# Insert your normal script here:

不要忘记设置一个有意义的值,datefile=seconds=根据您的需求调整其值($((60*60*24*3))评估期为3天)。


如果您不想使用单独的文件,也可以将上次执行时间存储在脚本的修改时间戳记中。但是,这意味着对脚本文件进行任何更改都会重置3计数器,并像脚本成功运行一样被对待。

要实现这一点,请将以下代码段添加到脚本文件的顶部:

#!/bin/bash

# Minimum delay between two script executions, in seconds. 
seconds=$((60*60*24*3))

# Compare the difference between this script's modification time stamp 
# and the current date with the given minimum delay in seconds. 
# Exit with error code 1 if the minimum delay is not exceeded yet.
if test "$(($(date "+%s")-$(date -r "$0" "+%s")))" -lt "$seconds" ; then
    echo "This script may not yet be started again."
    exit 1
fi

# Store the current date as modification time stamp of this script file
touch -m -- "$0"

# Insert your normal script here:

同样,请不要忘记根据seconds=您的需求调整价值($((60*60*24*3))评估期为3天)。


是的,记录特定程序的最后一次成功调用需要某种外部数据存储,而文件系统显然是这种选择。
吉莲·佛斯

甚至不需要存储日期-每次都只需触摸文件并检查其时间戳即可。
djsmiley2kStaysInside

@ djsmiley2k是的,您是对的。如果手动修改脚本文件导致延迟重置的风险是可以接受的,则也可以使用修改时间戳记。我将其添加到答案中。
字节指挥官

通过将当前时间戳保存到文件中(而不是人类可读的日期),可以略微打高尔夫球,因此您可以使用来获取经过时间$[ $(date +%s) - $(< lastrun) ]。如果脚本每天从cron运行一次,我可能会在所需的时间间隔上增加一些时间,以便如果脚本的执行延迟了几秒钟,那么下一次将不会跳过一整天。每天检查71小时是否经过oslt。
ilkkachu

带有datefile的向导确实有效,非常感谢!
塔维

12

Cron确实是错误的工具。实际上,有一个叫常用的underloved工具可能的工作。at 主要设计用于交互式使用,我相信有人会找到更好的方法来实现这一目的。

就我而言,我将在testjobs.txt中列出正在运行的脚本,并包含一行内容。

例如,我将其作为testjobs.txt

echo "cat" >> foo.txt
date >> foo.txt
at now + 3 days < testjobs.txt

我有两个无辜的命令,可能是您的shellscript。我运行echo以确保获得确定的输出,并输入日期以确认命令是否按需运行。在at运行此命令时,它将在3天的时间里向其添加新作业来完成。(我测试了一分钟-有效)

我敢肯定,我会叫出来我在虐待的方式,但它的调度命令一个方便的工具来运行一个时间x天前一个命令之后或。


3
+1 at似乎是一种更好的方法。这种方法的问题在于,每当您手动运行脚本时,您就会每三天添加另一组at任务。
Dewi Morgan Morgan 2013年

1
+1,但另一个问题(除了@DewiMorgan指出的问题之外)是,如果一个脚本失败,则所有后续脚本将不会启动(如果at在失败点之后),直到一个人意识到该脚本已失败并重新启动。这可能是不好的,有时甚至是好的(例如:失败是因为条件不再存在:最好在3天内不重试?)。每次都有一点漂移(如果atat长时间执行脚本的顶部,则为几毫秒;如果在长时间执行脚本的底部,则为更多)
Olivier Dulac

At可以发送电子邮件...。这可能是失败的解决方案。atq和atrm可能允许剔除错误的工作?从理论上讲,您可以为编写一个特定的日期,但这似乎不太可靠。
Journeyman Geek

因此,有一个cronjob会检查是否存在下一次​​运行时间:如果没有,请添加3天,然后发出警告(或立即运行并再次检查)。
djsmiley2kStaysInside

3

首先,上面的代码片段是无效的Bash语法,类似于Perl。其次,zdate使它输出数字时区的参数。+%j是天数。你需要:

if [[ ! $(( $(date +%j) % 3 )) ]] ;then
     exit
fi

但是到年底您仍然会感到陌生:

$ for i in 364 365  1 ; do echo "Day $i, $(( $i % 3 ))"; done
Day 364, 1
Day 365, 2
Day 1, 1

将文件计数并测试/更新可能会更好。


1
Perl没有date()内置函数,但是看起来有点像PHP中的date()z“一年中的某天(从0开始)”)
ilkkachu

2

如果您可以让脚本永久运行,则可以执行以下操作:

while true; do

[inert code here]

sleep 259200
done

此循环始终为true,因此它将始终执行代码,然后等待三天,然后再次开始循环。


为什么不while true呢?
乔纳森·莱夫勒

哈哈!接得好。我没有必要使用,在很长一段时间,我忘记了它的存在洛尔
mkingsbu

2
作为at解决方案,这将随每次运行脚本的执行时间而变化。当然,可以通过节省脚本启动时间以及从该时间开始休眠3天来解决此问题。
ilkkachu

2

您可以使用anacron代替cron,它完全可以满足您的需求。从联机帮助页:

Anacron可用于以天数指定的频率定期执行命令。与cron(8)不同,它不假定机器正在连续运行。因此,它可以在一天24小时不运行的机器上使用,以控制通常由cron控制的每日,每周和每月工作。

执行后,Anacron将从配置文件(通常为/ etc / anacrontab)中读取作业列表(请参阅anacrontab(5))。此文件包含Anacron控制的作业列表。每个作业条目都以天为单位指定时间段,以分钟为单位指定延迟,唯一的作业标识符以及shell命令。

对于每个作业,Anacron都会检查该作业是否在最近n天内执行过,其中n是为该作业指定的时间段。如果不是,那么在等待指定为delay参数的分钟数后,Anacron会运行作业的shell命令。

命令退出后,Anacron将日期记录在该作业的特殊时间戳文件中,因此它可以知道何时再次执行该日期。仅日期用于时间计算。不使用小时。


很好,我一定会测试Anacron。想知道为什么它能做到如此神奇呢?
Taavi

真正的问题:为什么现在不能用更好的东西代替cron?fcron存在,我认为systemd正在开发自己的解决方案,但我不了解当前的最新情况。
闪烁2016年
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.