2

I've got three SAS programs to run in the background over a weekend. The three programs output information into files of the same names, so I'll need to remove these output files after the first program finishes and before the second program starts, as well as after the second program finishes and before the third program starts. Obviously the simplest way to get around this issue is to alter each of the programs in such a way that the output filenames are different, but that wouldn't help me learn how to use Unix. So this is what I've got so far:

# Begin by running the first SAS program in the background.
sas program1.sas & | at 5:00 PM JAN 11

# I'd like to wait until the first program finishes to remove
# the output files and run the second program.
wait ???
rm file1.sas7bdat file2.sas7bdat file3.sas7bdat file4.sas7bdat file5.sas7bdat
sas program2.sas & | at 5:00 PM JAN 12

# And now I repeat.
wait ???
rm file1.sas7bdat file2.sas7bdat file3.sas7bdat file4.sas7bdat file5.sas7bdat
sas program3.sas & | at 5:00 PM JAN 13

I set each program to run at the same time on consecutive days for two reasons: (1) I know the previous program will finish within 24 hours, and (2) I am assuming the load on the server will be similar at the same time on consecutive days. Ideally I could somehow grab the process ID for the first program and pass it to the first wait command, and similarly for the second program and wait command. Any ideas?

EDIT

I am more interested in a general solution for determining a process ID and passing it to the wait command than this specific situation.

13
  • Run them in a different directory?
    – jordanm
    Commented Jan 10, 2013 at 20:28
  • @jordanm That's definitely an option, though that's really too problem specific. I provided the context for my problem because that's good practice and respectful to the community. However, my statement about somehow grabbing the process ID and passing it to the wait command is what I'm actually interested in answering. Commented Jan 10, 2013 at 20:35
  • @jordanm And now that I'm re-reading my post, I realize I started off with "My main problem ..." and didn't actually mention what I'm interested in until the end, so I'll edit the post. Commented Jan 10, 2013 at 20:39
  • Why dont you write a script this way: sas prg1; rm tmpfiles; sas prg2; rm tmpfiles; sas prg3? I'm not sure I understood your aims.
    – dchirikov
    Commented Jan 10, 2013 at 20:40
  • In bash you can use $! for getting last background job. But your scipt-example is not bash, right?
    – dchirikov
    Commented Jan 10, 2013 at 20:42

3 Answers 3

5

The problem is you're using at for something its not supposed to be used for, really. You also have two conflicting goals: "run program 2 after program 1" and "run program 2 at 5pm jan 12".

One part of your problem is simple to solve: at allows multiple commands. So, instead of just using at to schedule the sas script, you can put the rm in the job as well.

at 5:00 PM JAN 11 <<EOJ
sas program1.sas
rm file1.sas7bdat file2.sas7bdat file3.sas7bdat file4.sas7bdat file5.sas7bdat
EOJ

There is no reason for the & in the sas line; at always runs jobs "in the background".

Next, you must figure how you want to solve the conflict mentioned earlier. In particular, if the Jan. 11 job hasn't finished 24 hours later (by 5pm Jan. 12). There are a couple of ways to go about it:

  • It isn't really the Jan 12 job. It should run as soon as program1.sas finishes. In which case, just make it part of the first job above, just like the rm.
  • This will never happen. If it does, you'll fix it manually. In which case, just schedule it like the one above.
  • It needs to wait. In which case, you could either schedule it at the end of the first at job, or use a lock file, or calculate the appropriate amount of time to sleep (or use sleepenh, if available, to avoid doing the calculation yourself).
1
  • (+1) for a nice answer that points out logical flaws. Commented Jan 10, 2013 at 21:39
2

So what you have and want to do basically boils down to the following.

command1 &
wait <on command1>
sleep <until specified time>

command2 &
wait <on command2>
sleep <until specified time>

...

In this case, removing the backgrounding (&) as well as the wait and sleep will cause the commands to execute in sequence. This, however, will make them execute immediately after each other.

To wait until a specific time, you can sleep for the appropriate amount of time. sleep takes a number of seconds as input, and Unix systems traditionally keep and measure time in seconds, so this boils down to simple arithmetic:

  1. Convert the wait-for date and time to seconds since epoch.
  2. Convert the current time to seconds since epoch.
  3. Compute the difference.
  4. Sleep for that long.

How to do that is nicely laid out in this Stack Overflow answer, but to copy the essential part:

current_epoch=$(date +%s)
target_epoch=$(date -d '01/01/2010 12:00' +%s)
sleep_seconds=$(( $target_epoch - $current_epoch ))
sleep $sleep_seconds

That particular example is bash syntax, but it should be rather easy to convert to just about any shell scripting language. $(...) executes a command and replaces with the output of that command, and $(( ... )) evaluates an arithmetic expression.

GNU date -d also supports time-only timestamps, so you can say date -d '05:00' and it will translate that to the coming 05:00.

Putting this together, it should be easy to make a script like the one you want.

0
0

Well, there is no wait command. At least not that I know of. Here's my alternative logic:

Create 3 script files for each sas program. These scripts will only execute if the previous sas program has finished execution. This can be done by capturing pid of the previous sas program and verifying with ps command that it's not running anymore before removing the output files.

Schedule the scripts with crontab to be run on every Friday through Sunday, respectively.

So, if I named the first script file sas_script1.sh, it would look like this:

if [ -f /var/run/sas.pid ]; then
    /bin/ps $( cat /var/run/sas.pid ) >/dev/null
    [ $? ] && exit               #  $? checks the exit status of the last command
fi

rm -f /path/to/file{1..5}.sas7bdat

/usr/bin/sas /path/to/program1.sas &

echo $! >/var/run/sas.pid       #  $! gives you the PID of the last program that's sent background

Write the same script for rest of the two sas programs e.g just edit program1.sas to program2.sas

Create a file and write the following in it:

00 17 * * 5 /path/to/sas_script1.sh
00 17 * * 6 /path/to/sas_script2.sh
00 17 * * 0 /path/to/sas_script3.sh

Now, upload the file as cron job:

crontab /path/to/file

You can learn more about Un*X this way :D

6
  • "there is no wait command." man wait says otherwise. wait, waitpid, waitid - wait for process to change state.
    – user
    Commented Jan 10, 2013 at 21:11
  • @MichaelKjörling - help wait will show the help for the bash builtin. You are looking at the kernel system call wait(), not the shell's wait command.
    – jordanm
    Commented Jan 10, 2013 at 21:17
  • @jordanm Argh, you're right. Still, doesn't negate my point.
    – user
    Commented Jan 10, 2013 at 21:20
  • 2
    For the answerer, I downvoted this because there is a lot of bad practice here. The shell script is messy, likely to lead to stale lockfiles. [ $? ] - this does nothing since it's always true, even on a bad return code. It also uses non-POSIX features without specifying which shells it will work in.
    – jordanm
    Commented Jan 10, 2013 at 21:33
  • +1 not for the answerer but for @jordanm!
    – iruvar
    Commented Jan 11, 2013 at 5:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .