有一个可能会失败的流程,我希望在夜间(例如 23:00 到 7:00,但不能更晚)运行直至成功。
一个可能的解决方案是:
- 添加 23:00 的 cron 作业来启动帮助程序脚本。
- 在帮助程序脚本中,运行一个后台子 shell,它将重试该过程,直到成功为止。
- 在帮助程序脚本本身中,检查时间是否仍然小于 6:59,每分钟一次,如果更晚,则终止子 shell 及其所有子级。
然而,如果计算机在 23:00 关闭,并在 23:05 启动,这将失败,需要使用 bash 进行复杂的进程管理,需要每分钟唤醒,并且可能还需要锁定文件,所以一般是一种容易出错的方式。
是否有一些特殊的软件或类似的软件可以使定义和运行此类任务变得更容易?
答案1
一种可能的方法是每 5 分钟运行一次程序(例如),在第一次运行时创建文件作为正在运行的进程的标志,如果该标志存在,新的运行将退出。此外,如果程序成功,则创建其他文件作为标志,并且如果第二个文件存在,则不要运行该程序。不要忘记在程序成功或失败时删除第一个文件。
并在 07:00 运行 cron 来终止/停止程序(并删除成功文件标志(如果存在))
答案2
然而,如果计算机在 23:00 关闭,并在 23:05 启动,这将失败,需要使用 bash 进行复杂的进程管理,需要每分钟唤醒,并且可能还需要锁定文件,所以一般是一种容易出错的方式。
为了避免这个问题,使用anacron
而不是cron
我的发行版用于anacron
确保每日任务每天只运行一次(Ubuntu 20.04)
查看配置文件(并且仅查看日常任务)
/etc/crontab
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
这是一个故障安全条目(当未安装 anacron 时),在 06:25 运行每日脚本,但正如您正确提到的,系统必须在 06:25 打开才能正常工作。
什么时候anacron
是安装好了,看下配置文件。
/etc/anacrontab
# /etc/anacrontab: configuration file for anacron
# See anacron(8) and anacrontab(5) for details.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
HOME=/root
LOGNAME=root
# These replace cron's entries
# Frequency (in days) Delay JobId Command
1 5 cron.daily run-parts --report /etc/cron.daily
每天,启动后 5 分钟anacron
,运行 /etc/cron.daily 中的脚本。因此,如果我在 10:00 开启笔记本电脑并在 15:00 再次开启,则每日脚本仅运行一次,即anacron
10:00开始后 5 分钟
Ubuntu 使用systemd
而不是init
.因此,以下配置文件与了解如何anacron
启动相关。
/lib/systemd/system/anacron.service
[Unit]
Description=Run anacron jobs
After=time-sync.target
# By default, anacron will not run when no AC power is connected to system.
# If you are using systemd and want to run anacron even when running on
# battery, you should create the following file with the specified content
# and then call "systemctl daemon-reload":
# /etc/systemd/system/anacron.service.d/on-ac.conf:
# [Unit]
# ConditionACPower=
# See /usr/share/doc/anacron/README.Debian for detailed information.
ConditionACPower=true
Documentation=man:anacron man:anacrontab
[Service]
EnvironmentFile=/etc/default/anacron
ExecStart=/usr/sbin/anacron -d -q $ANACRON_ARGS
IgnoreSIGPIPE=false
KillMode=mixed
# Use SIGUSR1 to stop gracefully
KillSignal=SIGUSR1
[Install]
WantedBy=multi-user.target
最后是 /lib/systemd/system/anacron.timer
[Unit]
Description=Trigger anacron every hour
[Timer]
OnCalendar=*-*-* 07..23:30
RandomizedDelaySec=5m
Persistent=true
[Install]
WantedBy=timers.target
需要一点思维敏捷性才能头脑清醒,我确实喜欢@罗密欧·尼诺夫解决方案的简单性。缺点是每 5 分钟检查一次并退出,这会影响其可扩展性。
不过,我确实为我的笔记本电脑实现了每日备份解决方案(不是每天都打开),它运行得很好,它还允许我在启动之前打开一小会儿(我让延迟超过 5 分钟),所以我可以快速查看电子邮件,而不会触发潜在的新备份。
anacron -t <configfile>
如果您想避免篡改系统配置文件,您可以指定自己的配置文件。
了解更多信息:
man anacron
man anacrontab
答案3
最简单的答案是将at
命令与timeout
命令结合起来。
假设您的脚本的完整路径是$HOME/scripts/your-script.sh
默认的 shell 是bash
您的脚本假定在 1 秒到 7 小时或更长时间内完成。
您的脚本必须在 07:00AM 停止
为您的脚本创建一个带有超时的重试循环:$HOME/scripts/retry-your-script.sh
cat << EOF > $HOME/scripts/retry-your-script.sh
#!/bin/bash
success_marker=$HOME/scripts/success_marker
timeout 25200 '
while [[ ! -e "\$success_marker" ]]; do
$HOME/scripts/your-script.sh && touch \$success_marker
sleep 5s
done
'
EOF
chmod a+x $HOME/scripts/retry-your-script.sh
今天 23:00 运行您的重试脚本:$HOME/scripts/retry-your-script.sh
at 23:00 -f $HOME/scripts/retry-your-script.sh
扩展答案@罗密欧·尼诺夫并初步实施。
假设您的脚本的完整路径是$HOME/scripts/your-script.sh
默认的 shell 是bash
您的脚本假定完成时间为4分钟或更小。
创建一个名为 script 的 crontab:$HOME/scripts/croned-your-script.sh
cat << EOF > $HOME/scripts/croned-your-script.sh
#!/bin/bash
source $HOME/.bash_profile
success_marker=$HOME/scripts/success_marker
if [[ ! -e "\$success_marker" ]]; then
$HOME/scripts/timed-your-script.sh && touch \$success_marker
fi
EOF
chmod a+x $HOME/scripts/croned-your-script.sh
创建 crontab 清理脚本:$HOME/scripts/croned-cleanup.sh
cat << EOF > $HOME/scripts/croned-your-script.sh
#!/bin/bash
source $HOME/.bash_profile
success_marker=$HOME/scripts/success_marker
if [[ -e "\$success_marker" ]]; then
crontab -l | sed "/croned-your-script.sh/d" | crontab
crontab -l | sed "/croned-cleanup.sh/d" | crontab
rm -f "\$success_marker"
fi
EOF
chmod a+x $HOME/scripts/croned-cleanup.sh
将 crontab 脚本附加到您的 crontab 列表$HOME/scripts/croned-your-script.sh
crontab -l | sed "1i */4 22-23,0-6 * * * $HOME/scripts/croned-your-script.sh" | crontab
将清理 crontab 脚本附加到您的 crontab 列表$HOME/scripts/cron-cleanup.sh
crontab -l | sed "1i 1 6 * * * $HOME/scripts/croned-cleanup.sh" | crontab