I have a command CMD called from my main bourne shell script that takes forever.
I want to modify the script as follows:
- Run the command CMD in parallel as a background process ($CMD &).
- In the main script, have a loop to monitor the spawned command every few seconds. The loop also echoes some messages to stdout indicating progress of the script.
- Exit the loop when the spawned command terminates.
- Capture and report the exit code of the spawned process.
Can someone give me pointers to accomplish this?
A simple example, similar to the solutions above. This doesn't require monitoring any process output. The next example uses tail to follow output.
Use tail to follow process output and quit when the process is complete.
This may be extending beyond your question, however if you're concerned about the length of time processes are running for, you may be interested in checking the status of running background processes after an interval of time. It's easy enough to check which child PIDs are still running using
pgrep -P $$
, however I came up with the following solution to check the exit status of those PIDs that have already expired:which outputs:
Note: You could change
$pids
to a string variable rather than array to simplify things if you like.I would change your approach slightly. Rather than checking every few seconds if the command is still alive and reporting a message, have another process that reports every few seconds that the command is still running and then kill that process when the command finishes. For example:
As I see almost all answers use external utilities (mostly
ps
) to poll the state of the background process. There is a more unixesh solution, catching the SIGCHLD signal. In the signal handler it has to be checked which child process was stopped. It can be done bykill -0 <PID>
built-in (universal) or checking the existence of/proc/<PID>
directory (Linux specific) or using thejobs
built-in (bash specific.jobs -l
also reports the pid. In this case the 3rd field of the output can be Stopped|Running|Done|Exit . ).Here is my example.
The launched process is called
loop.sh
. It accepts-x
or a number as an argument. For-x
is exits with exit code 1. For a number it waits num*5 seconds. In every 5 seconds it prints its PID.The launcher process is called
launch.sh
:For more explanation see: Starting a process from bash script failed
The pid of a backgrounded child process is stored in $!. You can store all child processes' pids into an array, e.g. PIDS[].
Wait until the child process specified by each process ID pid or job specification jobspec exits and return the exit status of the last command waited for. If a job spec is given, all processes in the job are waited for. If no arguments are given, all currently active child processes are waited for, and the return status is zero. If the -n option is supplied, wait waits for any job to terminate and returns its exit status. If neither jobspec nor pid specifies an active child process of the shell, the return status is 127.
Use wait command you can wait for all child processes finish, meanwhile you can get exit status of each child processes via $? and store status into STATUS[]. Then you can do something depending by status.
I have tried the following 2 solutions and they run well. solution01 is more concise, while solution02 is a little complicated.
solution01
solution02
1: In bash,
$!
holds the PID of the last background process that was executed. That will tell you what process to monitor, anyway.4:
wait <n>
waits until the process with ID is complete (it will block until the process completes, so you might not want to call this until you are sure the process is done). Afterwait
returns, the exit code of the process is returned in the variable$?
2, 3:
ps
orps | grep " $! "
can tell you whether the process is still running. It is up to you how to understand the output and decide how close it is to finishing. (ps | grep
isn't idiot-proof. If you have time you can come up with a more robust way to tell whether the process is still running).Here's a skeleton script: