我的环境是 Ubuntu 22.04LTS 。要求是从现在或者某个时间点开始,每 7 分钟执行一次 test.sh 脚本。只能使用 crontab 来编写。只是讨论,真正实现的时候肯定用 bash 或者其他语言脚本更方便实现。

-------------2024.6.1----------------我们现在拥有了两种解法,如果还有其他解法请尽情探讨。一种穷举法,见#22 ,#32 ,#33 ,#34 ,#113一种复杂的 command 法,见#39 ,#40 ,#45 ,#51 ,#68 ,#96 ,#119-----------------------------穷举法:(需要增加偏移量的时候,调整周位置上的数字即可)#周一0-56/7 0-21/7 1 command3-59/7 1-22/7 1 command6-55/7 2-23/7 1 command2-58/7 3-17/7 1 command5-54/7 4-18/7 1 command1-57/7 5-29/7 1 command4-53/7 6-20/7 1 command#周二2-58/7 0-21/7 2 command5-54/7 1-22/7 2 command1-57/7 2-23/7 2 command4-53/7 3-17/7 2 command0-56/7 4-18/7 2 command3-59/7 5-29/7 2 command6-55/7 6-20/7 2 command#周三4-53/7 0-21/7 3 command0-56/7 1-22/7 3 command3-59/7 2-23/7 3 command6-55/7 3-17/7 3 command2-58/7 4-18/7 3 command5-54/7 5-29/7 3 command1-57/7 6-20/7 3 command#周四6-55/7 0-21/7 4 command2-58/7 1-22/7 4 command5-54/7 2-23/7 4 command1-57/7 3-17/7 4 command4-53/7 4-18/7 4 command0-56/7 5-29/7 4 command3-59/7 6-20/7 4 command#周五1-57/7 0-21/7 5 command4-53/7 1-22/7 5 command0-56/7 2-23/7 5 command3-59/7 3-17/7 5 command6-55/7 4-18/7 5 command2-58/7 5-29/7 5 command5-54/7 6-20/7 5 command#周六3-59/7 0-21/7 6 command6-55/7 1-22/7 6 command2-58/7 2-23/7 6 command5-54/7 3-17/7 6 command1-57/7 4-18/7 6 command4-53/7 5-29/7 6 command0-56/7 6-20/7 6 command#周日5-54/7 0-21/7 7 command1-57/7 1-22/7 7 command4-53/7 2-23/7 7 command0-56/7 3-17/7 7 command3-59/7 4-18/7 7 command6-55/7 5-29/7 7 command2-58/7 6-20/7 7 command-----------------------------复杂 command 法:(需要增加偏移量 N 的时候:((data+%s)/60+N)%7 ) * [ $(($(date +\%s) / 60 \% 7 )) -eq 0 ] && ( command )

根据#138 的提示。穷举法可以进一步缩写为:#周一0/7 0/7 1 command3/7 1/7 1 command6/7 2/7 1 command2/7 3/7 1 command5/7 4/7 1 command1/7 5/7 1 command4/7 6/7 1 command#周二2/7 0/7 2 command5/7 1/7 2 command1/7 2/7 2 command4/7 3/7 2 command0/7 4/7 2 command3/7 5/7 2 command6/7 6/7 2 command#周三4/7 0/7 3 command0/7 1/7 3 command3/7 2/7 3 command6/7 3/7 3 command2/7 4/7 3 command5/7 5/7 3 command1/7 6/7 3 command#周四6/7 0/7 4 command2/7 1/7 4 command5/7 2/7 4 command1/7 3/7 4 command4/7 4/7 4 command0/7 5/7 4 command3/7 6/7 4 command#周五1/7 0/7 5 command4/7 1/7 5 command0/7 2/7 5 command3/7 3/7 5 command6/7 4/7 5 command2/7 5/7 5 command5/7 6/7 5 command#周六3/7 0/7 6 command6/7 1/7 6 command2/7 2/7 6 command5/7 3/7 6 command1/7 4/7 6 command4/7 5/7 6 command0/7 6/7 6 command#周日5/7 0/7 7 command1/7 1/7 7 command4/7 2/7 7 command0/7 3/7 7 command3/7 4/7 7 command6/7 5/7 7 command2/7 6/7 7 command

/7 * test.sh

每隔 7 分钟执行一次? crontab.guru/#/7____*

60 不能被 7 整除,单纯用 cron 是不行的

为什么要考虑 60 被 7 整除,op 的需求只是间隔 7 分钟

cron 每分钟执行一次 sh ,在 sh 里判断时间

这个我知道,用两个 cron 表达式执行同一个命令
用 7 条 cron 把一个 420 分钟循环内落在每个小时里的分钟数都给写上,应该可以

sh 不能修改。实际上可能是我不能控制的已经封装好的程序。这里只是讨论单纯用 crontab 内能不能实现。用 sh 就很方便了。

/7 * /path/to/test.sh

cron 不行吧?/7 * 的话,在 17:56 会执行一次,在 18:00 又会执行一次了

可以把执行时间写入一个文件缓存,cron 每分钟调用 t1.sh ,t1.sh 里读取缓存做时间判断,再调用 test.sh

因为 cron 表达式 */7 只会在能被 7 整除的时候执行,举例来说 17:56 和 18:00 的时候都会执行,这可不是每 7 分钟运行一次的意思。

/7 * date >> /var/root/test.log加上了,半小时后我过来回复到底能不能用

明白你的意思了,等大佬解惑~

别用 cron, 不稳定, 不定什么时候就漏执行了

都上 sh ,不如直接 sh 内调用,然后 sleep 420 。循环就好。所以只是在讨论。实际用其他脚本语言会更快。

整个公倍数,每分钟都列出来
cron 每分钟 -> bash 脚本判断 -> 目标程序执行

如果解决不了问题那么就加一层这里假设楼上所说的不能被 60 整除的问题存在且不能单纯的使用 /7 * /path/to/test.sh 这种方式来解决问题可以再写一个脚本来调用 test.sh ,在那个脚本里面可以任意判断了。

cron 写每分钟运行,命令先判断当前时间是否整除然后再运行,bash 的各种离谱写法应该可以实现

crontab 没法实现

换天的时候会混乱。按照上面代码,第一天 23:48,23:55 分执行,第二天 00:00,00:07 执行.-------实际第二天应该 00:02,00:09 执行

五楼方案不是挺好的吗

确实,忘了考虑这点 😂

穷举法其实我想过,前面 4 位,分/小时/天/月,每次进位都会出现问题。因为 60 分/24 小时/28 天 or29 天 or30 天 or31 天/12 月 ,以上数字均不能被 7 整除形成循环。全部穷举后如果每年都是 365 天,7 年可以循环一次。但是实际上每 4 年有一次有闰年,也就是每 28 年才能穷举一次所有时间。

"都上 sh ,不如直接 sh 内调用,然后 sleep 420 。循环就好。所以只是在讨论。实际用其他脚本语言会更快。"不可靠!脚本运行命令以及等待命令返回都是要消耗时间的,并且这个时间还不确定。你可以每 sleep 420 将系统 RTC 时间写一下 log ,累积起来时间偏差会越来越大!所以应该通过不断(例如每 0.2s)读取 UNIX 时间戳方式判断是否经过了 7 秒。而不能去 sleep 7 秒。

感谢提醒,收到。确实 sleep 是在调用之后,会累计增加前面一句调用时间。

用 at 命令每次在脚本执行的时候先执行一下 at 命令echo "/path/to/myscript.sh" | at now + 7 minutes你的逻辑

你前面的,涉及时间计算的问题统一用 UNIX 时间戳进行。彻底解决各种烦恼!

用 5 楼的方式,每分钟扫描一次,获取时间戳,t = 时间戳毫秒/11000/60 ,再用对 t 对 7 取余

等等,我发现一周可以被 7 整除,而且一周这个单位不会被前面 4 个所影响。所以使用最后一个周作为标记来进行穷举即可。

不是还有星期这一位吗?考虑到跨天,写 49 条也能穷举了

我们只需要每天穷举 7 条 cron ,每周 7 天穷举 49 条 cron 就能实现。如果需要增加偏移量,只需要更改周的位置即可。

半个小时过去了

38 分钟了怎么说

systemd.timer 应该可以

#!/bin/shstart_time=$(date +%s)while true; do current_time=$(date +%s) elapsed_time=$((current_time - start_time)) if [ $elapsed_time -ge 420 ]; then ./myscript.sh start_time=$(date +%s) fi sleep 1done

          • ( [ -f /tmp/timestamp ] || date +\%s > /tmp/timestamp ) && [ $(( $(date +\%s) - $(cat /tmp/timestamp) )) -ge 420 ] && ( ./myscript.sh && date +\%s > /tmp/timestamp )

            后面 command 部分能接这种复杂的命令集吗?我尝试了下 cron 没运行(不知道是不是我的问题),但是直接输入后面的 command 是可以运行的。

            写绝对路径试试,/bin/date

cron 每分钟执行一次 sh 。 sh 里每次都判断特定文件夹里有几个文件。有 6 个文件时清空文件夹并执行实际代码。 不满 6 个文件时把当前 timestamp 作为文件名创建文件,不执行代码。这样就能 7 分钟一次了

定时每分钟执行,脚本里判断次数不就完了么。

先说清楚,间隔从执行开始算起还是执行后算起执行前算起的话,脚本执行超过 7 分钟如何处理

我尝试 * ((($(date +%s) / 60 % 7 == 0)) && /etc/test.sh) 并 [没有运行] 。我已经确认用户拥有 test.sh 的读写运行权限。另外我直接在 tty 里面输入((($(date +%s) / 60 % 7 == 0)) && /etc/test.sh)是 [可以运行] 的。

每隔 7 分钟,执行一次 test 脚本,不管 test 脚本之后的状态。

Ubuntu 22.04 不是有 systemd 么,timerOnUnitActiveSec 搞定,用啥 cron

该切到 systemd 的 timer 上了

如楼主回复所言,day of the week 可以被 7 整除,所以穷举一下是可以的,可能也就一千多条吧,换上用 slash(/)的 initial value 优化下也有几百条,真的合适么……

见 33 ,34 楼,只需要 49 条,还可以设置偏移量。

我找到一篇文章, href.lu/15A 。此文章表示在 macOS 中成功使用了复杂的 command 。但是我在我现在的 linux ubuntu 下没有运行成功。不知道是不是哪里错了,比如需要用引号之类的。

从每个小时的角度,每隔 7 分钟,就有 7 种开始时间。所以可以在每个小时开始时判断开始判断是那种,然后复写调度。

*/7 对吗,我猜的

比如 0 分钟开始,调度就是 0 ,07 ,14 ,21 ,28 ,35 ,42 ,49 ,56 ,以及下个小时的 03 。在 63 的时间点,判断是不是个位,如果是,则继续加到下一个小时,并覆盖 crontab

每 7 分钟一次,不能能用简单整除的方法,我想到的:- 要么就穷举一次循环,把循环分钟记录下来作为判断;- 要么就每次执行完写一次时间到文件,下次运行的时候从这个文件判断要不要是不是已间隔 7 分钟。

cron 做不到,脚本内循环判断时间戳/60 后对 7 取余是否为 0 即可。但你要保证每次执行业务逻辑都能在 7 分钟内完成才行。

复写的时候是哪段代码或什么外部程序或者谁(真人)来进行。

#57 你可以写到/etc/crontab 里,每次删除最后一条再把新的插入进去

#57 就是你自己的这个脚本本身需要有权限去写。开权限就可以了

cron 每分钟执行一次 sh ,在 sh 里判断时间就算能修改 sh 好像也不行呀,看上下文基本可以确认是个无状态的任务(那就假设它是吧),sh 里怎么知道上次执行是啥时候,隔了多久咧

要操作外部脚本啊,那就不在讨论范围了。都能用外部脚本了,方法就多了,楼上很多。

Thu May 30 17:07:00 CST 2024Thu May 30 17:14:00 CST 2024Thu May 30 17:21:00 CST 2024Thu May 30 17:28:00 CST 2024Thu May 30 17:35:00 CST 2024Thu May 30 17:42:00 CST 2024Thu May 30 17:49:01 CST 2024Thu May 30 17:56:00 CST 2024Thu May 30 18:00:00 CST 2024Thu May 30 18:07:00 CST 2024Thu May 30 18:14:00 CST 2024Thu May 30 18:21:00 CST 2024Thu May 30 18:28:00 CST 2024Thu May 30 18:35:00 CST 2024Thu May 30 18:42:00 CST 2024实测只用 /7 * 确实不行 - -
*/7 不行,如果一定要实现间隔,貌似只能 外部引入一个 sleep_7_minutes.sh 实现

#61 $schedule->command('b2buhrenApi:productSync') ->when(intval(time()/60) % 7 ==0);这个是老外的回答,你试试 laracasts.com/discuss/channels/laravel/how-to-run-schedule-every-7-minutes?page=1&replyId=821790

最小公倍数原理

#61 又找了一个[[ $(($(date +'%s / 60 % 7 '))) -eq 0 ]] &&

我试试

#61 * [[ $(($(date +'%s / 60 % 7 '))) -eq 0 ]] && command 前面的这个并上你的命令,每分钟做判断,如果是就执行

写文件,把上次运行的时间写到文件里,我看很多程序就把自身运行的 pid 写到文件里,很常规的操作吧

bash: syntax error near unexpected token `(' 是我的环境问题吗

你可以看看 45 楼和 51 楼,我不确定是不是我环境或者什么问题,在 ubuntu 下无法执行。

#70 减掉一层括号试试

另外这个算没用 cron 了吧。

要能这么玩我估计不会有本帖 hhh

#34 学到了

          • source /etc/profile && ( [ -f /tmp/timestamp ] || date +\%s > /tmp/timestamp ) && [ $(( $(date +\%s) - $(cat /tmp/timestamp) )) -ge 10 ] && ( echo 2 && date +\%s > /tmp/timestamp )

            #73 #70 我在服务器上测试是可以的[[ $(($(date +'%s / 60 % 7 '))) -eq 0 ]] && echo hello 。另外这个是目前最简单的不用引用外部的做法

            同样,在 cron 里面 * [[ $(($(date +'%s / 60 % 7 '))) -eq 0 ]] && command 。 [无法运行] ------在 tty 内,[[ $(($(date +'%s / 60 % 7 '))) -eq 0 ]] && command 。 [正常运行]
            我也可以直接在 tty 中使用这行 command ,但是 cron 里面不会运行

            #79 你检查下 crontab 是不是写对的

          • [[ $(($(date +'%s / 60 % 1 '))) -eq 0 ]] && /etc/test.sh 我将%7 改为%1 ,这样理论上每分钟就会运行一次 test.sh 。但是没有运行

            见楼上,我已经正确赋予了用户对 test.sh 的读写运行权限。另外如果我改为 * /etc/test.sh 是可以正常每分钟运行的。

            同时,直接在窗口运行 [[ $(($(date +'%s / 60 % 1 '))) -eq 0 ]] && /etc/test.sh是没问题的,因为左边部分永远为真,右边始终会执行。

            那就写到脚本里面呗,每分钟执行 1 次脚本,脚本里[[ $(($(date +'%s / 60 % 7 '))) -eq 0 ]] || exit

            如果你那边服务器可以运行,请告诉我以下你服务器的系统和版本号。我去装一个测试下,看是不是只有 ubuntu 不行。

执行完 sleep 7*60

#83 我也试试,crontab 好像是不太行,我改了几次命令,好像还是执行不了

每分钟跑一次,在程序里判断时间点/7 的余数为某个值就执行

一周有 7 天所以用多(但是有限)个 cron 表达式合起来应该可以每 7 分钟一次

#88 估计和平台有关 。#51 楼我找到一个文章,声称使用 macOS 可以这么运行。但是没有发现 linux 平台下的案例。

不会是面试题吧?要么加一层,然后写入上次执行的时间,如果也不能写文件,那就放弃吧,纯 crontab 肯定实现不了

换个思路systemd-run --on-boot=30 --on-unit-active=420 bash /path/script.shRunning timer as unit: run-r2f2f454d18944d79af858a4be4f48cd5.timerWill run service as unit: run-r2f2f454d18944d79af858a4be4f48cd5.service如果测试 ok,把对应的 timer 和 service 从/run/systemd/transient/ move 到/etc/systemd/system 做持久化

最简单就是每分钟运行,然后脚本获取当前时间,分钟能被 7 整除就运行。否则就退出。如果是其他时间 60 能被 n 整除可以用*/n

我想了个双脚本思路,大致内容如下script_a.sh:#!/bin/bash# 使用 at 命令在 7 分钟后执行脚本 b#run taskecho "/path/to/script_b.sh" | at now + 7 minutesscript_b.sh:#!/bin/bash# 使用 at 命令在 7 分钟后执行脚本 a#run taskecho "/path/to/script_a.sh" | at now + 7 minutes

[[ $(($(date +'%s / 60 % 7 '))) -eq 0 ]] &&这句在 cron 里有几个问题:1 ,[[是 bash 扩展,不是命令,所以在 cron 的 shell 环境里报错,改用[ ... ]或者test ...应该就没问题了。[1]2 ,字面%在 cron 里应该用\转义。[2]所以,下面这条应该可以:*/1 * * * * root [ $(($(date +'\%s / 60 \% 7 '))) -eq 0 ] && ( echo "CRON RUN" | logger )[1] stackoverflow.com/a/47576482/6681141[2] serverfault.com/questions/274475/escaping-double-quotes-and-percent-signs-in-cron

写复杂命令在 cron 里面可以实现,但是这个和套一层 shell 有啥本质区别?无非是把外层的判断写在了 cron 行上面了而已

直接写 bash 脚本 cron 本来就是用在规律用途 也比较鸡肋 因为 bash 本身就能实现 cron 的功能 有准确的用法 至于 sleep 为何会不准因为是外部命令 启动要时间 用久会有大误差 这需求有不只一种实现方法systemd? 垃圾东西就不要用了

哎,就喜欢这样的 v2

用脚本吧,sleep ( 7 * 60 ),然后异步执行