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?

1 Answer 1

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:

4
  • 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 9, 2019 at 4:57
  • Yes, @daisy, you are right. I've tested your idea and it works like a charm. The answer have been updated.
    – pa4080
    Jan 9, 2019 at 7:32
  • Hello, @daisy, have you find any better solution for this job?
    – pa4080
    Jan 24, 2019 at 9: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, 2019 at 5:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.