16

I have the following bash script, we can call it script1.sh:

#!/bin/bash

exec ./script2.sh &

sleep 5

if job1 is alive then #<--- this line is pseudo-code!
    exec ./script3.sh &
    wait
fi

As can be seen, the script executes script2.sh as a background job and then waits 5 seconds (so script2.sh can do some initialization stuff). If the initialization succeeds, the script2.sh job will still be alive, in which case I also want to start script3.sh concurrently; if not, I just want to quit.

However, I do not know how to check whether the first job is alive, hence the line of pseudo-code. So, what should go in its place?

XåpplI'-I0llwlg'I -
  • 21,649
  • 28
  • 102
  • 151
  • 3
    Running exec will not do what you think. It will replace the current process with a new process; later lines in your script won't be reached. – Todd A. Jacobs Jun 28 '12 at 07:13

6 Answers6

15

You can get the PID of the most recent background job with $!. You could then check the exit status of ps to determine if that particular PID is still in the process list. For example:

sleep 30 &
if ps -p $! >&-; then
    wait $!
else
    jobs
fi
Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
  • 2
    That exact command gives me the error ps: write error: Bad Filedescriptor – FORTRAN Dec 03 '14 at 07:04
  • This doesn't always work. For example, in Alpine Linux, the default "ps" is bound to BusyBox, which does not support the -p switch. Using `bash` built-ins like `jobs` is more portable. – Gary Wisniewski Aug 19 '15 at 01:27
  • 1
    I get a 'ps: write error: Bad file descriptor' from the `>&-` which appears to happen because there isn't a numeric file descriptor `M` in the `M>&-` that is supposed to close file descriptor `M`. I used `>/dev/null 2&>1` instead. – Dave X Jan 18 '19 at 17:21
  • 3
    Or check if pid exist in /proc dir `exec ./script2.sh &; pid=$!; [[ -e /proc/$pid ]] && { yor_code; }` – Ivan Jan 10 '20 at 07:05
  • I see @Ivan suggestion as more elegant and resilient against distro differences and should be in an answer by itself. Thanks! – Elton Carvalho May 25 '20 at 21:46
  • 1
    @EltonCarvalho, thank you, added this as an answer. – Ivan May 26 '20 at 07:02
  • Another trick to check if a process is running is to use "kill -0 $!". It'll "exit 0" if the process exists (and you have permission to send it a signal). – Capt. Crunch Jun 19 '20 at 06:39
  • Strongly tempted to downvote because this is the least reliable answer. It is technically useful, but it is also inherently not the right way to solve this problem. – mtraceur Jul 17 '20 at 13:32
13

You can check if a signal is deliverable

./script2 &
myPid=$!
sleep 5

if kill -0 "$myPid"; then
    script3 &
    wait
fi
ormaaj
  • 6,201
  • 29
  • 34
10

To expand on fduff's answer you can use the jobs builtin:

if jobs %%; then
    exec ./script3.sh &
    wait
fi

jobs %% prints the job ID of the most recent background process (the "current job") and returns 0 on success or 1 if there is no such job.

Aaron D. Marasco
  • 6,506
  • 3
  • 26
  • 39
Gerald Combs
  • 1,374
  • 10
  • 12
  • 1
    Todd A. Jacobs' approach failed on my Ubuntu 18.04 system (`Bad file descriptor`) and ormaaj's worked, but `kill -0` can fail (i.e., non zero return code) for 2 reasons: you don't have permission or the PID isn't running. AFAIK, `jobs` is a "cleaner" (Occam's razor) answer because (1) it works and (2) "failure" doesn't depend on your permissions but on the PID not running, thus reducing the error-checking code you need to write. – mellow-yellow Mar 13 '19 at 19:51
  • If you have to check only for one background job this works fine. Also, it is an easy solution if you run your script in a PID namespace with unshare. In the latter case $! would return the PID inside the namespace and ps -p $! would not find it in the list of pids. I haven't found (yet) easy alternatives for this case. – matteo martelli Jun 19 '19 at 16:22
  • @mellow-yellow We have to think beyond documented errors. For the majority of uses, a permissions failure is logically impossible. If you're passing job specifiers instead of PIDs to `kill` you eliminate the possibility of race conditioning yourself into hitting another user's process due to PID reuse. I'm not even aware of any way for you to not have permissions to signal your own child process. That said, I agree that if you wanted to be maximally proper, proper use of `jobs` (assuming it is portable enough for the use case) is probably better. – mtraceur Jul 17 '20 at 13:42
5

I am not sure exec works with background, as it replaces the image of the parent process with that of the child process, otherwise, if we assume you get rid of exec, you'd want something like:

#!/bin/bash
./script2.sh&
pid1=$!

if kill -0 $pid1; then
  ./script3.sh&
  pid3=$!
  wait
fi
bobah
  • 18,364
  • 2
  • 37
  • 70
  • You should use job specifiers instead of PIDs whenever possible for greater reliability: `kill -0 %1` for the first job, `kill -0 %%` for the most recently spawned job, etc. (The key limitation is that you must know what the job number or specifier was, which in some scripts might be harder to know or predict at the spot where you're starting the background job than the PID.) – mtraceur Jul 17 '20 at 13:50
  • if it's harder to use why are you saying I should use it? – bobah Jul 17 '20 at 16:15
  • The answer is in my first sentence: "for greater reliability". A more appropriate question would be "in what ways does it increase reliability?". I also didn't say it's harder to use. I said that it *might be* harder to use *in some cases*. But a logical implication of "for greater reliability" is that it is actually harder to use the PID *correctly* or in ways that would get the same reliability that a jobspec would give you. With PIDs there is a race condition with PID reallocation. Very unlikely, but we have to think through what the consequences for our script or users would be if it did. – mtraceur Jul 17 '20 at 19:10
  • Actually, after some contemplation, I think I want to walk my claim back a little bit: **if you never call `wait` between starting the child process and checking the PID with `kill`**, then you should be safe. Because the PID of a child process (**but not grandchild process** or any other process besides direct children of our process, etc) cannot get reused out from under us by external activity until we've collected its exit status with a `wait`. – mtraceur Jul 17 '20 at 19:17
3

Copy your background process pid into a var

./script2.sh &; pid=$!

And then check if this pid exist in /proc dir

[[ -e /proc/$pid ]] && { your_code; }

Or with ls

ls /proc/$pid 2>/dev/null && { your_code; }
Ivan
  • 6,188
  • 1
  • 16
  • 23
1

Have a look at the jobs command; it lists all running jobs.

Ben Mosher
  • 13,251
  • 7
  • 69
  • 80
fduff
  • 3,671
  • 2
  • 30
  • 39