1

I have a script which runs every 5 seconds, Is it possible to run this script between for example CET 08:00 - 12:00?

I thought I could do this with a cron job but that has a minimum of 1 minute unfortunately. So I was thinking of creating a cron job which starts the script at 08:00 and then kills it 12:00, I assume that is the best solution.

Anyone has any input?

    #!/bin/bash

while true; do
    python3 script.py
    sleep 5;
done
Altakes
  • 11
  • 3
  • 1
    Does this answer your question? [Cron jobs -- to run every 5 seconds](https://stackoverflow.com/questions/3331577/cron-jobs-to-run-every-5-seconds) – accdias Feb 23 '20 at 20:00
  • 1
    That will wait for N seconds before each call which will drift from a true N-second period. So should it be *N-seconds apart* or *every* N-seconds? – user2864740 Feb 23 '20 at 20:10
  • N-seconds apart I think, doesn't matter If I understood your question correctly. Run the script, wait 5 seconds and then run it again. That is what I am trying to achieve :) – Altakes Feb 23 '20 at 20:17

2 Answers2

3

As pointed out by Wiimm his answer only covers the "run the script, wait 10 seconds and repeat, between 8:00 and 12:00".

There are several approaches I can think of:

A. start the script at 8:00 in cron and have it check when it's time to finish

#!/bin/sh

END=$(date -d "12:00" +"%H * 60 + %M" | bc)

while [ $(date +"%H * 60 + %M" | bc) -lt $END ]; do
    date >> /tmp/log.txt # replace this with your script 
    sleep 10
done

Explanation:

  • date +"%H * 60 + %M" | bc computes the minutes since the day started. Note: bc is used here to handle leading zeros $(( $(date +"%H * 60 + %M") )) would error out.
  • [ $(date +"%H * 60 + %M" | bc) -lt $END ] - compares with the time to end

B. start it from cron and use a signal handler to handle a graceful exit, use cron to end it.

#!/bin/bash

echo $$ > /tmp/pid.txt
EXIT=
trap "echo 'Exiting' >> /tmp/log.txt; EXIT=yes" TERM

while [[ -z "$EXIT" ]]; do
    date >> /tmp/log.txt # replace this with your script
    sleep 10
done

crontab would look like this:

00 08 * * * /path/to/wrappers/script.sh
00 12 * * * kill $(cat /tmp/pid.txt)

Explanation:

  • echo $$ > /tmp/pid.txt - saves the pid (you should probably chose another location, appropriate for your system)
  • trap "echo 'Exiting' >> /tmp/log.txt; EXIT=yes" TERM - this will run the echo 'Exiting' >> /tmp/log.txt; EXIT=yes when a TERM signal is received (TERM is the default signal sent by kill).
  • while [[ -z "$EXIT" ]]; do - repeat while EXIT is empty
  • this has the advantage of being able to gracefully shut down the script when needed, not only at 12:00.

Note: a combination of both would be probably the best approach.

Also: see https://stackoverflow.com/a/53561843/939457 - it details how to run this via SystemD, but that will run the script every 10 seconds even if the previous script didn't end (if I understand the answer correctly, I'm not familiar with SystemD)

Sorin
  • 5,201
  • 2
  • 18
  • 45
  • Cron or anacron are really the only good solutions: bash shouldn't handle this, ideally. Cron exists solely to schedule tasks at specific times and do so reliably. Let it do its job, and then let the script encompass just the functionality it needs. – Alex Huszagh Feb 23 '20 at 23:48
  • @AlexanderHuszagh - cron doesn't have the 10 seconds resolution, and would be unable to run the script 10 seconds after the previously run ended – Sorin Feb 23 '20 at 23:50
  • Here the job is not executed every 10 seconds, but executed every 10 seconds plus the time needed to execute the commands. See my answer for an alternative for `sleep 10`. – Wiimm Feb 24 '20 at 06:39
  • @Sorin Title says: "run every 10 seconds". And don't see my comment+answer as critics, see it as note/idea for an alternative to fulfill the request by title. I upvoted your solution because I like it. – Wiimm Feb 24 '20 at 07:44
1

In addition to the solution of Sorin.

while true; do
  do_something 
  sleep 10
done

Here the job do_something is not (!) executed every 10 seconds, but executed every 10 seconds plus the time needed to execute do_something. So an alternative is:

while true; do
  sleep $((10-$(date +%s)%10))
  do_something 
done

Here the job do_something is executed every time, when seconds is one of 0, 10, 20, 30, 40 or 50. The only pitfall: If do_something runs mode than 10 seconds, at least one execution is skipped.

Explanation of my sleep:

DELAY=10
sleep $(( DELAY- $(date +%s) % DELAY))

$((...)) enters math mode. $(date +%s) returns the total number of seconds since epoch. % DELAY is a modulo operation, so that the result is between 0 and excluding DELAY(0..9 in this example). DELAY- guaranties that the sleep time is at least 1 second.

Wiimm
  • 2,971
  • 1
  • 15
  • 25
  • 1
    $SECONDS has a special meaning in bash (actually, I'm not sure if it's bash only, but I don't have time to look it up): _"Each time this parameter is referenced, the number of seconds since shell invocation is returned. If a value is assigned to SECONDS, the value returned upon subsequent references is the number of seconds since the assignment plus the value assigned. If SECONDS is unset, it loses its special properties, even if it is subsequently reset."_ You might want to consider a different name for your example, or use it yo your advantage – Sorin Feb 24 '20 at 08:08
  • @Sorin: Thanks for the hint. I know that, but don't thought about it while writing the explanation. But my example will work, because a local definition of SECONDS overrides the special meaning by bash. Anyway, I replaced it by DELAY now to avoid confusions. – Wiimm Feb 24 '20 at 09:27
  • https://ideone.com/pPSPoy - you have to explicitly unset SECONDS for it to work – Sorin Feb 24 '20 at 09:32
  • 1
    try `echo $SECONDS; sleep $(( 10 - $SECONDS % 10 )); echo $SECONDS` or in a **new shell** something like `while true; do SECONDS=0; sleep $(( $RANDOM % 4 + 1)) ; echo $SECONDS; sleep $((10 - $SECONDS)); echo $SECONDS; done` - and I looked it up, it seems that posix sh doesn't have it – Sorin Feb 24 '20 at 09:42