3

I've tried this solution to prevent duplicate cronjob from running.

30,31 12 * * * /usr/bin/flock -n /tmp/my.lockfile ~/Desktop/test.sh >> ~/Desktop/test.log 2>&1

and script test.sh has

#!/bin/bash

for i in {1..40};do
    echo "hello$i"
    sleep 2
done

Script test.sh successfully prevented from running at 12:31 but it stops and I want to resume it. How can I make it only suspend, not stopped and resume back? Is it possible?

d a i s y
  • 5,511

1 Answers1

2

You can add a test, in the beginning of the script, to check whether there are another early started instances of the script and while this is true the current instance will sleep. Something like that:

while ps aux | grep -v 'grep' | grep "$0" | sed -e "/$$/p" -e "/$$/,$ d" | grep -vq "$$"
do sleep 1
done

Where:

  • the variable $0 contains the script name (and execution path);

  • the variable $$ contains the PID of the current running instance of the script;

  • ps aux - output the current processes;

  • grep -v 'grep' - preserve the lines that contains grep within the output of the previous command;

  • grep "$0" - output only the lines that are related to the current script name;

  • sed -e "/$$/p" -e "/$$/,$ d" - remove the newer instances of the script; remove all lines after the line that contains the PID of the current instance;

  • grep -vq "$$" - this is the actual test -q that will return 0 (true - there is at least one older instance of the script) or 1 (false - apparently this is the newest existing instance of the script) when the line with PID of the current instance is removed -v.

Here is a complete example:

#!/bin/bash

while ps aux | grep -v 'grep' | grep "$0" | sed -e "/$$/p" -e "/$$/,$ d" | grep -vq "$$"
do
        sleep 1
        echo -n '.'
done

date +"%T"

for i in {1..5};do
    echo "hello$i"
    sleep 2
done

Here is the test I've made:

enter image description here

In addition you can create a launcher script that will execute your actual script in the above manner.

$ cat ~/Desktop/test-cron-launcher.sh 
#!/bin/bash

LAUNCH_TIME="$(date +"%T")"

while ps aux | grep -v 'grep' | grep "$0" | sed -e "/$$/p" -e "/$$/,$ d" | grep -vq "$$"
do sleep 1
done

echo "Job from $LAUNCH_TIME begin at $(date +"%T")"

. "$HOME/Desktop/test.sh"
$ cat ~/Desktop/test.sh 
#!/bin/bash
for i in {1..40};do
    echo "hello$i"
    sleep 2
done
$ crontab -l | grep 'test'
30,31 12 * * * "$HOME/Desktop/test-cron-launcher.sh" >> "$HOME/Desktop/test.log" 2>&1

Read also:

pa4080
  • 29,831
  • Okay so I've to create another script like cronjob.sh which contains your solution while ..do..done with source ~/Desktop/test.sh and crontab entry will be like 30,31 12 * * * ~/Desktop/cronjob.sh >> ~/Desktop/test.log 2>&1, right? – d a i s y Jan 09 '19 at 04:57
  • Yes, @daisy, you are right. I've tested your idea and it works like a charm. The answer have been updated. – pa4080 Jan 09 '19 at 07:32
  • Hello, @daisy, have you find any better solution for this job? – pa4080 Jan 24 '19 at 09:28
  • No, I did not find any other solution. I'd upvoted already and as mentioned in question this is just test script, I've not yet applied to real scenario as it happen either every after six months or once in a year. If I'd asked a question a few days ago then I could. – d a i s y Jan 29 '19 at 05:41