0

There is a certain potentially failing process that I want to run until it succeeds, during the night time, say, from 23:00 to 7:00, but not later.

A possible solution would be to:

  1. Add a cron job for 23:00 to start the helper script.
  2. In the helper script, run a background subshell which would be retrying the process until it succeeds.
  3. In the helper script itself, check that the time is still <6:59, every minute and if it is later, kill the subshell with all its children.

This would, however, fail if the computer is off at 23:00, and is started, say, at 23:05, would require complex process management with bash, would require waking up every minute, and maybe requiring lock files too, so is generally an error-prone way.

Is there some special software, or something like that, which would make defining and running such tasks easier?

3
  • is this a follow on from unix.stackexchange.com/questions/684261/…
    – Bravo
    Commented Dec 29, 2021 at 9:24
  • 2
    why not simply make the program create a marker file when it succeeds? And then, each next run to check for the existence of the marker file, if it's there, don't run again.
    – Bart
    Commented Dec 29, 2021 at 9:25
  • @Bravo these questions are similar, but not necessarily solvable in the same way, it seem to me. Commented Dec 29, 2021 at 13:44

3 Answers 3

1

One possible way is to run program every 5 minutes (for example), on first run create file as flag of running process and if the flag exist new run will exit. Also if program succeed create other file as flag and do not run the program if this second file exist. Do not forget to remove first file on program end successful or not.

And at 07:00 run cron to kill/stop the program (and delete succeed file flag if exist)

1

This would, however, fail if the computer is off at 23:00, and is started, say, at 23:05, would require complex process management with bash, would require waking up every minute, and maybe requiring lock files too, so is generally an error-prone way.

To avoid this problem use anacron instead of cron

My distribution uses anacron to ensure that the daily tasks are only run once per day (Ubuntu 20.04)

Looking at the configurations files (and only looking at daily tasks)

/etc/crontab

25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )

This is a failsafe entry (when anacron is not installed), run the daily scripts at 06:25 , but as you correctly mentioned the system must be switched on at 06:25 for this to work.

When anacron is installed, look at the next configuration file.

/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

Daily, 5 mins after starting anacron, run the scripts in /etc/cron.daily. So, if I switch my laptop on at 10:00 and again at 15:00 the daily scripts are only run once, 5 mins after starting anacron at 10:00

Ubuntu uses systemd instead of init. So the following config files are relevant to understand how anacron is started.

/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

And finally /lib/systemd/system/anacron.timer

[Unit]
Description=Trigger anacron every hour

[Timer]
OnCalendar=*-*-* 07..23:30
RandomizedDelaySec=5m
Persistent=true

[Install]
WantedBy=timers.target

It takes a bit of mental agility to get your head around, and I do like @Romeo Ninov's solution for it's simplicity. The downside is something starting every 5 mins to check and exit, which affects it's scalability.

However I did implement a daily backup solution for my laptop (not switched on every day) an it worked very well, it also allowed me to switch on for a short time before it kicked in (I made the delay more that 5 mins) so I could check my email quickly without triggering a potential new backup.

You can specify your own config file anacron -t <configfile>, if you want to avoid tampering with the system configuration file.

For more information:

man anacron
man anacrontab
0

Simplest answer is to combine at command with timeout commands.

Assuming your script's full path is $HOME/scripts/your-script.sh

And the default shell is bash

Your script assumes completion in 1s up to 7h or more.

Your script must stop at 07:00AM

create a retry loop with timeout for your script: $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

run your retry script at 23:00 today: $HOME/scripts/retry-your-script.sh

at 23:00 -f $HOME/scripts/retry-your-script.sh

Expand on answer from @Romeo Ninov's with initial implementation.

Assuming your script's full path is $HOME/scripts/your-script.sh

And the default shell is bash

Your script assumes completion in 4 min or less.

Create a crontab called script: $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

Create a crontab cleanup script: $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

Append to your crontab list, a crontab script from $HOME/scripts/croned-your-script.sh

crontab -l | sed  "1i */4 22-23,0-6 * * * $HOME/scripts/croned-your-script.sh" | crontab

Append to your crontab list, a cleanup crontab script from $HOME/scripts/cron-cleanup.sh

crontab -l | sed  "1i 1 6 * * * $HOME/scripts/croned-cleanup.sh" | crontab

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .