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 |
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
Are you new to LinuxQuestions.org? Visit the following links:
Site Howto |
Site FAQ |
Sitemap |
Register Now
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
|
|
|
10-17-2016, 08:12 AM
|
#1
|
Member
Registered: Aug 2012
Posts: 789
Rep:
|
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.
|
|
|
10-17-2016, 08:45 AM
|
#2
|
Moderator
Registered: Aug 2002
Posts: 26,127
|
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.
|
|
|
10-17-2016, 08:50 AM
|
#3
|
Member
Registered: Aug 2012
Posts: 789
Original Poster
Rep:
|
Quote:
Originally Posted by michaelk
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
|
|
|
10-17-2016, 08:51 AM
|
#4
|
Member
Registered: Nov 2010
Location: Minneapolis, MN
Distribution: Ubuntu, Red Hat, Mint
Posts: 172
Rep:
|
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.
|
10-17-2016, 08:58 AM
|
#5
|
Member
Registered: Aug 2012
Posts: 789
Original Poster
Rep:
|
Quote:
Originally Posted by thesnow
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
|
|
|
10-17-2016, 09:05 AM
|
#6
|
Moderator
Registered: Aug 2002
Posts: 26,127
|
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.
|
|
|
10-17-2016, 05:35 PM
|
#7
|
Member
Registered: Nov 2007
Location: Northeastern USA
Distribution: kubuntu
Posts: 214
Rep:
|
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.
|
|
|
10-18-2016, 07:36 AM
|
#8
|
Member
Registered: Aug 2012
Posts: 789
Original Poster
Rep:
|
Quote:
Originally Posted by michaelk
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$
|
|
|
10-18-2016, 07:39 AM
|
#9
|
Member
Registered: Aug 2012
Posts: 789
Original Poster
Rep:
|
Quote:
Originally Posted by josephj
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.
|
|
|
10-18-2016, 08:01 AM
|
#10
|
Senior Member
Registered: Jan 2003
Location: Illinois (SW Chicago 'burbs)
Distribution: openSUSE, Raspbian, Slackware. Previous: MacOS, Red Hat, Coherent, Consensys SVR4.2, Tru64, Solaris
Posts: 2,837
|
Quote:
Originally Posted by NotionCommotion
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.
|
10-18-2016, 08:11 AM
|
#11
|
Member
Registered: Aug 2012
Posts: 789
Original Poster
Rep:
|
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
|
|
|
10-18-2016, 08:42 AM
|
#12
|
Moderator
Registered: Aug 2002
Posts: 26,127
|
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 &
|
|
|
10-18-2016, 09:20 AM
|
#13
|
Member
Registered: Aug 2012
Posts: 789
Original Poster
Rep:
|
Quote:
Originally Posted by michaelk
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.
|
|
|
10-18-2016, 10:49 AM
|
#14
|
Moderator
Registered: Aug 2002
Posts: 26,127
|
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.
|
10-19-2016, 11:51 AM
|
#15
|
Senior Member
Registered: Jan 2003
Location: Illinois (SW Chicago 'burbs)
Distribution: openSUSE, Raspbian, Slackware. Previous: MacOS, Red Hat, Coherent, Consensys SVR4.2, Tru64, Solaris
Posts: 2,837
|
Quote:
Originally Posted by NotionCommotion
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.
|
|
|
All times are GMT -5. The time now is 06:40 AM.
|
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.
|
Latest Threads
LQ News
|
|