This is a read-only archive. Find the latest Linux articles, documentation, and answers at the new Linux.com!

Linux.com

Feature: Linux

CLI Magic: Wait or Sleep?

By Jim Westbrook on March 28, 2005 (9:00:00 AM)

Share    Print    Comments   

Usually, you want a shell script to just run to completion, one command after another. There are times, however, when a sequence of events includes a step upon which subsequent steps depend for successful execution. For these times, two useful commands are wait and sleep, both of which cause a delay in the script execution. Updated code samples
The wait command man page simply states that it pauses script execution until a specified process ID completes. The man page for the sleep command is a single line stating that it causes a script to pause for specified period of time.

How do you decide which is appropriate for the script at hand? In some cases it won't make any difference to the successful completion of the script. Other times it can be the difference between success and total failure to produce the expected results.

As an example of a script in which the difference is potentially significant, let's use a root cron script that runs every day to update a database of file locations. (Please note that this is an example only, not something I have debugged and actually run.)

  #! /bin/sh 
cd /stuff/flix 
ftp <*.mpg files from a source> 
updatedb 
locate mpg |grep /stuff/flix 
exit 0

While this would appear to be a valid construction, it will fail if executed, because the line beginning with locate would be called before the previous line had run to completion. On a large system the updatedb process can take several minutes. This is the perfect time to use the wait command.

  #! /bin/sh 
cd /stuff/flix 
sftp <*.mpg files from a source> &
wait 
updatedb &
wait 
locate mpg |grep /stuff/flix 
exit 0 

A call to wait after the sftp command delays the script until all file transfer activity has completed. Similarly, inserting wait after the updatedb call delays further processing until the updatedb process finishes.

Why not use the sleep command instead? In the above illustration, having the script delay for only a specified amount of time would work only if the amount of time specified was sufficiently long for the task to complete. You would have to guess how much time would be required for the longest transfer of files. Since that time can vary significantly due to many factors, even if the file size(s) are exactly the same every day, the use of wait insures that the process has completed.

So when is the sleep command the right choice? Let's look at another example, this time snipped from the /etc/init.d/smb script, to illustrate the choice. Note: these code samples are for illustration only!

# wait for cupsd to be ready 
function wait_for_cupsd 
{ 
        echo -n "Samba SMB: Waiting for cupsd to get ready" 
        SAMBA_CUPSD_TIMEOUT=30 
        rc_timer_on ${SAMBA_CUPSD_TIMEOUT} 42 
        START=$( date +%s) 
        while [ $(( $( date +%s) - ${START} )) -lt ${SAMBA_CUPSD_TIMEOUT} ]; 
do 
                lpstat -r &>/dev/null 
                LPSTAT_RC=$? 
                if [ ${LPSTAT_RC} -eq 0 ]; then break 
                else sleep 2 
                fi 
        done 
        rc_timer_off 

        test ${LPSTAT_RC} -eq 0 && rc_status -v || rc_status -s 
        rc_reset 
} 

This script calls the sleep command within a while/do/done loop with a parameter of two seconds as the else branch of an if/then/else test. This causes the loop to re-execute every two seconds until the printer daemon is ready to accept print jobs. Here we know exactly how long we wish to wait before testing the status again.

That's the real difference in making the choice between wait and sleep -- knowing the amount of time to delay the script.

Now, armed with the knowledge of what the wait and sleep commands can do, and how to decide which to use, we come to the fun part of script usage -- finding applications for what we have learned that can make the computing experience more productive or just less hassle. Any multi-step computing task that you perform frequently is a good candidate for becoming a script.

A cron tip

Because I mentioned the use of a cron job earlier, here's a little comment header that can make life a lot easier when setting your crontab. I use a file called cronset that I can edit easily. I then call crontab passing it cronset as a command-line parameter.

 
MAILTO="" 
#################################################################### 
#minute (0-59),                                                    # 
#|      hour (0-23),                                               # 
#|      |       day of the month (1-31),                           # 
#|      |       |       month of the year (1-12),                  # 
#|      |       |       |       day of the week (0-6 with 0=Sunday)# 
#|      |       |       |       |       commands                   # 
#################################################################### 
30      01      *       *       *       /home/jim/bin/cleartmp 

Jim Westbrook has been BBS sysop for seventeen years, a network admin since 1983, and a Linux user and advocate since 1999. He is the current president of the Austin Linux Group, in Austin, Texas.

Share    Print    Comments   

Comments

on CLI Magic: Wait or Sleep?

Note: Comments are owned by the poster. We are not responsible for their content.

sorry, I totally disagree

Posted by: Anonymous Coward on March 28, 2005 06:11 PM
with wait, you wait for child processes. To create such a process run it in the background, with appending an ampersand & to the command line. Processes like sftp and updatedb run in the foreground and will return when they are ready so will the wait return immediately because there are no child processes to wait for. In other words your example is really useless and to say you haven't run or debugged the code is a very poor excuse.


You either want to teach us something then do it properly. Or you think that we can figure out the bugs ourselves then we don't need your tutorial.

#

Huh?

Posted by: Anonymous Coward on March 28, 2005 06:17 PM
I can only assume that some ampersands were missing from the article. The bash builtin wait simply
waits for a child process that is running in the -background- to finish. Running a bunch of commands
in series with no ampersands (i.e. they are running in the foreground) is not a terribly good example of what wait is used for (since the waits do absolutely -nothing-). However, if you wanted to run a command in the background, do some other things, then wait for that original command to finish, wait would be what you would want to use.

For example:
<TT>#!/bin/sh
updatedb &
echo "Doing some other stuff"
sleep 5
# now that we've finished some things, lets wait for our child processes to finish
wait</TT>
I have a feeling the author is a bit misinformed on the use of the wait command since none of the examples have meaningful uses of it.



- Andy

#

Re:Huh?

Posted by: Anonymous Coward on March 28, 2005 10:10 PM
Heck, you could even do that one as:
<TT>#!/bin/sh
updatedb &
echo "Doing some other stuff"
sleep 5
fg</TT>
(right?)

#

Re:Huh?

Posted by: Anonymous Coward on April 01, 2005 07:52 PM
Yeah, quite. ISTM inconsistent - either updatedb forks and must be waited-for, or you need the & on the end. Examples of just one command being backgrounded and waited-for are pretty pointless - just foreground it and the shell will wait for you.

Now, if you want a real-life example, I've got a shell-script that backs-up subdirectories from over 20 different locations on the network - lines of the form:

rsync -e ssh otherbox:/some/dir/ backups/some/dir/ -arvP --delete

so the network utilization is a trickle of rsync traffic (a few K/s) per process. Does anyone think I'm going to wait for each one to finish (sometimes an hour or more) before moving on to the next? Like heck. I background them all and then, in between bunches of 5-6 or so (grouped by similar expected time-to-run), wait (as the article doesn't say - wait without any pid-spec waits for all backgrounded child processes to return). Hence at any point in time, up to 5-6 rsync pids will be running, collating stuff from all over the place, utilizing bandwidth nicely... script takes a fraction of the total time to run, too.

#

This is ridiculous!

Posted by: Anonymous Coward on March 28, 2005 09:01 PM
Why not a wait after the 'cd<nobr> <wbr></nobr>...' too, remember this is not a real time OS. You can't always assume that sequential things follow the rules.<nobr> <wbr></nobr>;-)


I can only agree with my pre-commenters, either there something got terrible mixed up during the edit of the article, or I have to assume a terrible lack of knowledge.


And while I'm bashing [SCNR] the author,

<TT>(Please note that this is an example only, not something I have debugged and actually run.)</TT>
if you can't get a five-liner right, you shouldn't write such an article.<nobr> <wbr></nobr>:-(


I can only advice to either correct or remove this article from the site!

- dev

#

Here is a good example for wait

Posted by: Anonymous Coward on March 29, 2005 01:18 AM
Here is a good example for "wait" and "sleep" which I sometimes use. This script waits for the command "cmd_to_run" to finish. If it doesn't finish within 5 seconds, the command gets killed automatically. This is great for commands that can hang.

cmd_to_run="sleep 10"

$cmd_to_run &
cmd_pid=$!

(sleep 5; kill $cmd_pid 2>/dev/null) 2>/dev/null &
bg_pid=$!

wait $cmd_pid;
kill $bg_pid ><nobr> <wbr></nobr>/dev/null 2>&1

#

Re:Here is a good example for wait

Posted by: Anonymous Coward on March 29, 2005 06:28 PM
I am no expert, but what if ur cmd_to_run terminates before the 5 sec time limit, then another process of yours starts and it is given the same pid as the terminated cmd_to_run? Then at the end of the 5 sec period u will kill a differend process.

#

Re:Here is a good example for wait

Posted by: Anonymous Coward on March 30, 2005 04:33 AM
By default, processes are normally assigned PIDs sequentially, without regard to dead processes. E.g., if process 1000 is started, it's killed, and a new process starts, the new process will get PID 1001. PIDs are only reused once the 32767 limit is reached, so it is plausible that two processes started in 5 seconds could have the same PID only if 32765 other processes are running.

I believe, though, that some security enhanced kernel patches assign PIDs randomly instead of sequentially. While this in and of itself isn't a problem, I don't know if they still prevent reuse of PIDs.

#

Good uses: multiple rsync's and backup

Posted by: Anonymous Coward on April 07, 2005 05:55 AM


The last time I used <tt>wait</tt> in production
code was for a custom back-up script for a customer.


In this case he had several small servers (for various functions) and one system with an HP 4mm DAT autochanger. I would fire up one process to do <tt>mtx eject && mtx load $NEXT_TAPE</tt> in the background (enclosing that sequence of commands in parentheses, and adding the <tt>&</tt> operator, of course). Then I had a loop that looked something like:

<TT>for i in $SERVER_LIST; do
   set -- $i
   rsync -e ssh -avz $1:$2<nobr> <wbr></nobr>/var/backups/$2 &
   done</TT>
<nobr> <wbr></nobr>... then I had my <tt>wait</tt> command to wait until all of these tasks were complete. This allowed all the <tt>rsync</tt> and <tt>mtx</tt> commands to run concurrently while still ensuring that all of these commands were complete before attempting to do the <tt>dump</tt> (and the <tt>restore -C</tt> to verify the dump's integrity).


This is the proper use of <tt>wait</tt>. It allows a series of sub-processes to work concurrently and allows us to synchronize on the completion of all of them or selected jobs.


In this case we want to be sure that all of the <tt>rsync</tt> processes are complete (to subdirectories on the local disk) before dumping those subdirectory trees off to tape.


I wrote that script a over a year ago, and it was a fairly quick 1 day hack with another day or two of testing. So far as I know it's been in use by that customer ever since and they haven't needed to call for support after the first month or two of ironing out a few minor testing issues.



JimD
LinuxGazette "Answer Guy"

#

Simple Example for Teaching

Posted by: Anonymous Coward on April 07, 2005 06:02 AM


Here's a simple example that you can run just from the command line without even saving it in a script. Understand this and you'll understand the basics of <tt>wait</tt>:

<tt>(sleep 5; echo "First job") & (sleep 2; echo "Second job") & wait; echo "After waiting"</tt>


This will print "Second job" then "First job" and then "After waiting" in that sequence due to how the jobs synchronize with one another.


I use this for teaching shell scripting.


Jim Dennis,

Linux Gazette "Answer Guy"

#

This story has been archived. Comments can no longer be posted.



 
Tableless layout Validate XHTML 1.0 Strict Validate CSS Powered by Xaraya