72

So I have a process running, and it will take several hours to complete. I would like to start another process right after that one finishes, automatically. Notice that I can't add a call to the second script in the first one, neither create another which sequentially runs both. Is there any way to do this in Linux?

Edit: One option is to poll every x minutes using pgrep and check if the process finished. If it did, start the other one. However, I don't like this solution.

PS: Both are bash scripts, if that helps.

Community
  • 1
  • 1
skd
  • 1,865
  • 1
  • 21
  • 29

8 Answers8

67

Given the PID of the first process, the loop

while ps -p $PID; do sleep 1; done ; script2

should do the trick. This is a little more stable than pgrep and process names.

thiton
  • 35,651
  • 4
  • 70
  • 100
  • 19
    I would modify this to not flood the terminal: `echo Waiting...; while ps -p $PID > /dev/null; do sleep 1; done; script2` – bmikolaj Sep 30 '14 at 21:52
60

Maybe you can press ctrl+z first and enter

fg; echo "first job finished"
ki9
  • 5,183
  • 5
  • 37
  • 48
Yangchuan Li
  • 601
  • 5
  • 2
  • 6
    A pragmatic (and probably very widely used) approach. – hagello Feb 23 '16 at 21:13
  • 1
    I like this approach because it is simple. Can you explain how the semi-colon works with fg? Is this the same as having run "myjob && mysecondjob" initially? – E. Schiesser Dec 19 '16 at 23:23
  • 5
    @ScheissSchiesser the `;` separates commands, whatever they may be. So in this case, `fg` brings the suspended job to the foreground, and completes it; then the second command is run. Like you mention, you could also do `fg && mysecondjob` which would launch the second job only if the first (resumed) job returns a 0 exit code (i.e. completes successfully). – jayelm Jul 21 '17 at 07:18
  • 1
    Much better than polling every second to see if the job has finished or not. And works without having to fish for the pid! – Peeyush Kushwaha Apr 24 '18 at 19:20
  • This is really great! – justhalf May 11 '19 at 18:43
  • This should have been the accepted best answer rather than the polling suggestion. – Frank van Wensveen Apr 20 '21 at 19:40
34

Polling is probably the way to go, but it doesn't have to be horrible.

pid=$(ps -opid= -C your_script_name)
while [ -d /proc/$pid ] ; do
    sleep 1
done && ./your_other_script
sorpigal
  • 25,504
  • 8
  • 57
  • 75
  • @Tom Zych: Your oneliner relies on the user entering both commands at the same time. This will work after the first command has been independently executed (e.g. by a cron job, or spawned from a noninteractive process). – sorpigal Sep 20 '11 at 13:35
  • Oh. I thought you were complaining about it being a script. Sorry. – Tom Zych Sep 20 '11 at 13:36
  • This is the solution I was thinking of, however using wait seems better ``wait $PID && second_script``. I don't know how ``wait`` is actually implemented so it may be the same. – skd Sep 20 '11 at 13:38
  • 1
    @skd: `wait` is better if you can use it, but IIRC it only works for sub-processes, not arbitrary PIDs (which is too bad). – sorpigal Sep 20 '11 at 13:44
  • why do you need to have sleep 1 here, can you use something like echo > /dev/null 2>&1 ? Also, – olala Nov 17 '14 at 00:46
  • @olala: it depends on how many cycles you want to burn. With GNU sleep you can also specify a fraction, e.g. `0.1`. How important is reacting quickly vs. efficiency? You decide. – sorpigal Nov 25 '14 at 02:21
  • 4
    I disagree. Polling is almost _never_ the way to go. With polling, there will always be discussion about the delay in the waiting loop: one second? A tenth of a second? Your choice will probably be OK for some use case but less than optimal for other use cases. However, UNIX/bash provides the way to do it _right_: `wait`. `wait` suspends your job until another process ends and then your job will be resumed _immediately_. No arbitrary delay. Subito. No loop necessary. _Except_ if the job you want to wait for is not started in the same shell. – hagello Feb 23 '16 at 20:56
  • @skd `wait` in bash is implemented using the system call `wait4`. You can see it if you `strace bash`. – hagello Feb 23 '16 at 21:10
  • 2
    I accepted this answer since this is what I ended up doing. However, I agree that wait is probably always the way to go. This seems to be a popular question still, so if someone comes here looking for an answer just use wait as @hagello and more suggested. – skd Feb 24 '16 at 10:28
  • 1
    This doesn't work if the process have already stopped or never run. And this seem not work if I have multiple process on same name/script_name. – SharkIng Jul 14 '16 at 18:19
  • 1
    @SharkIng: if the process may have already terminated you can replace `&&` with `;`; if it hasn't run yet there's really no way to be sure unless you can check for some side effect. If you may have multiple instances of the script running you must make some decisions about whether you're waiting for all to finish, for any, etc.. This will complicate the script. – sorpigal Jul 21 '16 at 17:35
  • Does while consume any CPU or memory? @Sorpigal – alper Apr 28 '17 at 08:21
  • 1
    @Avatar: All software uses CPU and memory. The loop above will not use much of either; it will not spin up one core to 100% if that's what you're worried about. The sleep interval prevents this. – sorpigal Apr 29 '17 at 22:02
23

You can wait already running process using bash built-in command wait. man bash.

wait [n ...] Wait for each specified process and return its termination status. Each n may be a process ID or a job specification; if a job spec is given, all processes in that job's pipeline are waited for. If n is not given, all currently active child processes are waited for, and the return status is zero. If n specifies a non-existent process or job, the return status is 127. Otherwise, the return status is the exit status of the last process or job waited for.

ks1322
  • 33,961
  • 14
  • 109
  • 164
2

Often it happens that your program is running several demons. In that case your pid will be an array. Just use:

PID=($(pidof -x process_name)) #this saves all the PIDs of the given process in the $pid array

Now, just modify the thiton's code as :

while ps -p ${PID[*]}; do sleep 1; done ; script2

shivams
  • 923
  • 12
  • 27
0

I had a similar problem and solved it this way:

nohup bash script1.sh &

wait

nohup bash script2.sh &

0

I had the same requirement and solved it in the following way:

while [[ "$exp" != 0 ]]; do
exp=$(ps -ef |grep -i "SCRIPT_1" |grep -v grep |wc -l)
sleep 5;
done

call SCRIPT_2

-2

The easiest way:

./script1.sh && ./script2.sh

The && says wait for the successful completion of script1 before proceeding to script2.

Cosmtar
  • 516
  • 5
  • 14
  • 2
    This doesn't answer the question, namely, that `script1.sh` is already running, and OP doesn't want to restart that process. – jayelm Jul 21 '17 at 07:19