LinuxQuestions.org
Latest LQ Deal: Linux Power User Bundle
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie
User Name
Password
Linux - Newbie This Linux forum is for members that are new to Linux.
Just starting out and have a question? If it is not in the man pages or the how-to's this is the place!

Notices


Reply
  Search this Thread
Old 10-17-2016, 08:12 AM   #1
NotionCommotion
Member
 
Registered: Aug 2012
Posts: 709

Rep: Reputation: Disabled
Creating shell script to mimic CRON job and execute an external file


Hi,

I wish to create a shell script which executes a PHP file ever 15 seconds. Below is my attempt, and while it "kinda" works, it needs help.

First, I wish only one instance to ever be running. Currently, I can have multiple instances all running at once and updating the database causing havoc.

Second, I haven't figured out how to implement stop, restart, and status, and everything causes it to start.

I appreciate any help. Thanks

vi /etc/init.d/15_second_cron
Code:
#!/bin/bash
# /etc/init.d/15_second_cron

### BEGIN INIT INFO
# Provides:          15_second_cron
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: PHP Loop
# Description:       Initiate a given PHP file which updates the database every 15 seconds
### END INIT INFO

while true
do
 /usr/bin/php /var/www/cron.php &
 sleep 15
done
exit 0
Code:
# Make script executable
chmod +x /etc/init.d/15_second_cron

/etc/init.d/15_second_cron start
chkconfig --levels 235 15_second_cron on

Last edited by NotionCommotion; 10-17-2016 at 08:13 AM.
 
Old 10-17-2016, 08:45 AM   #2
michaelk
Moderator
 
Registered: Aug 2002
Posts: 16,024

Rep: Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850
The update appears to take more then 15 seconds and since the script is sent to the background it allows multiple instances to run. To keep things simple I would run it from rc.local and not as a service.
 
Old 10-17-2016, 08:50 AM   #3
NotionCommotion
Member
 
Registered: Aug 2012
Posts: 709

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by michaelk View Post
The update appears to take more then 15 seconds and since the script is sent to the background it allows multiple instances to run. To keep things simple I would run it from rc.local and not as a service.
I don't expect the script will take more than 15 seconds. Even if it did, I would still want it to execute on 15 second intervals. The reason I put it in the background is so the 15 second increments will be maintained regardless of the the length it took.

What do you mean by rc.local versus a service?

Thank you
 
Old 10-17-2016, 08:51 AM   #4
thesnow
Member
 
Registered: Nov 2010
Location: Minneapolis, MN
Distribution: Ubuntu, Red Hat, Mint
Posts: 170

Rep: Reputation: 56
Your script is sending the PHP execution to the background, so right after it starts so does the sleep timer. If your PHP job ever takes more than 15 seconds, it will kick another one off before the first one ends. If you remove the "&", the sleep timer won't start until the PHP script completes.

Can you clarify what "every 15 seconds" means. Is this on the :00, :15, :30, etc.? If the script takes 5 seconds then the next iteration would only be 10 seconds after the first one completes.

Or 15 seconds after the PHP script finishes you want to start the next one. For example, if it starts at 0 and runs for 5 seconds, then the next iteration would start on :20?
 
1 members found this post helpful.
Old 10-17-2016, 08:58 AM   #5
NotionCommotion
Member
 
Registered: Aug 2012
Posts: 709

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by thesnow View Post
Can you clarify what "every 15 seconds" means. Is this on the :00, :15, :30, etc.? If the script takes 5 seconds then the next iteration would only be 10 seconds after the first one completes.

Or 15 seconds after the PHP script finishes you want to start the next one. For example, if it starts at 0 and runs for 5 seconds, then the next iteration would start on :20?
I wish to have records written to a database which are 15 seconds apart. The actual time is not important, and it is also okay if they are not exactly 15 seconds apart, but only close. Thanks
 
Old 10-17-2016, 09:05 AM   #6
michaelk
Moderator
 
Registered: Aug 2002
Posts: 16,024

Rep: Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850
Depends on the distribution but rc.local is a script and actually a "service" but it is the last thing to run before x starts. Typically you add your commands to /etc/rc.local file.

You can use the time command to determine how long the script actually runs and adjust the sleep variable accordingly although without putting it in the background that should satisfy your 15 seconds apart.

Last edited by michaelk; 10-17-2016 at 09:09 AM.
 
Old 10-17-2016, 05:35 PM   #7
josephj
Member
 
Registered: Nov 2007
Location: Northeastern USA
Distribution: kubuntu
Posts: 140

Rep: Reputation: 79
michaelk and thesnow point out the details of why your script/configuration is not working as desired.

You can't have something scheduled by cron to start every 15 minutes if it also sleeps and starts itself in 15 minutes and, most importantly, never ends. You have to do it one way or the other.

It's also important, as thesnow indicates, to keep track of when things really run. You can debug this by writing to a log file somewhere with date/time stamps to see when things really happen. There are a lot of things which can influence when a process is run and how long it takes to complete.

Once things work, you can disable the log if you don't need it for other things, but you might want to leave the code in for the next time things change.
 
Old 10-18-2016, 07:36 AM   #8
NotionCommotion
Member
 
Registered: Aug 2012
Posts: 709

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by michaelk View Post
Depends on the distribution but rc.local is a script and actually a "service" but it is the last thing to run before x starts. Typically you add your commands to /etc/rc.local file.

You can use the time command to determine how long the script actually runs and adjust the sleep variable accordingly although without putting it in the background that should satisfy your 15 seconds apart.
Ah, I see how I can locate it in rc.local. This should work. Wouldn't I still want it in the background? Otherwise, I will be 15 seconds plus the time it takes to execute apart.

Also, while I "could" put in in rc.local, why wouldn't I wish to create a service, and have it start upon server startup? It seems more (not that I would know) professional?

Thanks!

Code:
-sh-4.1$ cat /etc/rc.local
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local
-sh-4.1$
 
Old 10-18-2016, 07:39 AM   #9
NotionCommotion
Member
 
Registered: Aug 2012
Posts: 709

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by josephj View Post
You can't have something scheduled by cron to start every 15 minutes if it also sleeps and starts itself in 15 minutes and, most importantly, never ends. You have to do it one way or the other.
I am not using a cron job. The service runs constantly, and initiates an event ever 15 seconds (seconds, and as such a cron won't work).

The part I really needed help on is how to implement start, stop, restart, and status. Right now I just have start, and if I attempt to start it twice, it will do so. Instead, if I try to start it twice, I wish to be told "this service is already running" and not to attempt to start it again.

Thanks

EDIT. I know this is not correct, but end result is something like this but will actually work

Code:
#!/bin/bash
# /etc/init.d/15_second_cron

### BEGIN INIT INFO
# Provides:          15_second_cron
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: PHP Loop
# Description:       Initiate a given PHP file which updates the datalogger database every 15 seconds (fix and find a better way!)
### END INIT INFO

# Are functions required???
. /etc/rc.d/init.d/functions

prog=15_second_cron
???? pidfile=${PIDFILE-/var/run/httpd/httpd.pid}
???? lockfile=${LOCKFILE-/var/lock/subsys/httpd}
RETVAL=0

        echo -n $"Starting $prog: "
        
        while true
        do
         /usr/bin/php /var/www/datalogger/cron.php &
         sleep 15
        done
        
        # Will never get here???
        
        RETVAL=$?
        echo
        [ $RETVAL = 0 ] && touch ${lockfile}
        return $RETVAL
}

stop() {
        ??? status -p ${pidfile} $httpd > /dev/null
        if [[ $? = 0 ]]; then
                echo -n $"Stopping $prog: "
                killproc -p ${pidfile} -d ${STOP_TIMEOUT} $httpd
        else
                echo -n $"Stopping $prog: "
                success
        fi
        RETVAL=$?
        echo
        [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}
}

case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  status)
        status -p ${pidfile} $httpd
        RETVAL=$?
        ;;
  restart)
        stop
        start
        ;;
esac

exit $RETVAL

Last edited by NotionCommotion; 10-18-2016 at 08:07 AM.
 
Old 10-18-2016, 08:01 AM   #10
rnturn
Senior Member
 
Registered: Jan 2003
Location: Illinois (Chicago area)
Distribution: Red Hat (8.0, RHEL5,6), CentOS, SuSE (10.x, 11.x, 12.2, 13.2), Solaris (8-10), Tru64, MacOS, Raspian
Posts: 1,137

Rep: Reputation: 65
Quote:
Originally Posted by NotionCommotion View Post
Instead, if I try to start it twice, I wish to be told "this service is already running" and not to attempt to start it again.
One simple way to do this is to create lock file in say, /var/tmp, that you create just before you launch the background task. Modify the PHP script to delete the lock file just before it exits. This could be a fixed/statically named file or something you generate on the fly and pass off to the PHP script ("Hey, delete this file when you're done.") If your parent script wakes up and sees the lock file then one of two things have happened: the PHP task isn't finished yet (sleep for another 15s and recheck for the lock file) or the PHP script crashed before removing the lock file. Not sure how you'd want to deal with the last situation.

You might want to add some additional code to count how many times in a row the parent script woke up to find that the lock file was still there and maybe notify you that something's taking too long. That might be a way to deal with the PHP script crashed case: bail after N retries. There are a number of bells and whistles you could add to this.
 
1 members found this post helpful.
Old 10-18-2016, 08:11 AM   #11
NotionCommotion
Member
 
Registered: Aug 2012
Posts: 709

Original Poster
Rep: Reputation: Disabled
Thanks rnturn,

Is the lock file like how the apache script uses: lockfile=${LOCKFILE-/var/lock/subsys/httpd}

What is this file all about? pidfile=${PIDFILE-/var/run/httpd/httpd.pid} all about?

In addition to doing this, my goal is also to learn a proper and repeated approach to creating these scripts. Is there a good one to follow as a model?

Thanks
 
Old 10-18-2016, 08:42 AM   #12
michaelk
Moderator
 
Registered: Aug 2002
Posts: 16,024

Rep: Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850
It will start at boot using rc.local. In my opinion using an init script is over kill but it can be done.

Have you actually timed your php script to see how long it runs? You want it to run every 15 secs but prevent multiple instances. Since you posted that your script is causing multiple instances that would indicate the php script is taking more then 15 seconds.

Two keep this simple you can use the time command to see how long it runs and then compute the necessary sleep time interval.

The problem is the while loop in your init script. Your service is the while loop which should be separate from the init script. Untested but something like:

Code:
  while true
  do
    t=$( /usr/bin/time -f "%e" "/usr/bin/php /var/www/cron.php" )
    i=$( echo "scale=0; 15-$i" | bc )
    if (( $i>0 ))
    then
       sleep $i
    fi
 
done
You can use the daemon command to put the script in the background from your init script or in the rc.local with &
 
Old 10-18-2016, 09:20 AM   #13
NotionCommotion
Member
 
Registered: Aug 2012
Posts: 709

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by michaelk View Post
Have you actually timed your php script to see how long it runs? You want it to run every 15 secs but prevent multiple instances. Since you posted that your script is causing multiple instances that would indicate the php script is taking more then 15 seconds.
Ah, I see the source of confusion, and apologize for it.

I don't think the script will take anything close to 15 seconds, but even if it did take more, that is fine.

I wish the script to start at 15 second intervals. If it takes 20 seconds, then yes, there will be two PHP processes running at the same time for 5 seconds, and this is okay.

What I don't want is first starting the shell script service so the PHP runs at 15 second intervals, and then starting the shell script service again so that a who new series of 15 second intervals starts.
 
Old 10-18-2016, 10:49 AM   #14
michaelk
Moderator
 
Registered: Aug 2002
Posts: 16,024

Rep: Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850Reputation: 1850
The pid file contains the running process ID of the program. Typically used by the init script to see if the program is already running.

A lock file is used used to indicate that a certain resource is in use and should not be accessed by another process.

Another idea would be to add the timing loop to your cron.php script. I would also make it executable and run it from /usr/bin/local.

http://www.thegeekstuff.com/2012/03/lsbinit-script/
 
1 members found this post helpful.
Old 10-19-2016, 11:51 AM   #15
rnturn
Senior Member
 
Registered: Jan 2003
Location: Illinois (Chicago area)
Distribution: Red Hat (8.0, RHEL5,6), CentOS, SuSE (10.x, 11.x, 12.2, 13.2), Solaris (8-10), Tru64, MacOS, Raspian
Posts: 1,137

Rep: Reputation: 65
Quote:
Originally Posted by NotionCommotion View Post
Is the lock file like how the apache script uses: lockfile=${LOCKFILE-/var/lock/subsys/httpd}
I'm not sure how Apache uses that lock file (or why it's needed). You typically use lock files to prevent multiple processes from using a resource that can only handle one user at a time. Or to prevent multiple instances of some process from running likely because of one of those single-user-only resources or the process writes to a fixed file/location and having multiple copies of it running would create scrambled data (i.e., a log file may now be your single-user-only object).

There are some who pooh-pooh lock files (we occasionally get into discussions about their (dis)advantages at work) but, when you're writing scripts, they're (IMHO) the simplest way to control access to single-user-only objects.

Quote:
What is this file all about? pidfile=${PIDFILE-/var/run/httpd/httpd.pid} all about?
That kind of file is usually something you see created when a service (like Apache) is started. The Apache shutdown script will look for that file and read the process ID contained in it so it knows what process to send "kill" signals to.

Quote:
In addition to doing this, my goal is also to learn a proper and repeated approach to creating these scripts. Is there a good one to follow as a model?
Probably not one of mine.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Cron job script creating corrupt archive roach7711x Linux - Server 2 04-08-2009 04:01 AM
I can execute shell script from CLI but failed to do via cron UltraSoul Linux - Software 2 06-24-2008 10:10 PM
shell script using /etc/cron.hourly to execute cron.php file? rioguia Programming 3 06-11-2008 08:09 AM
How to set up cron job to execute bash script lgmqy2000 Linux - General 4 11-22-2006 04:29 AM
shell script fo run auto job in cron JolynnMarie LinuxQuestions.org Member Intro 0 04-28-2004 11:21 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie

All times are GMT -5. The time now is 10:28 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration