21

(Former Title - bash: Run command in background and capture pid)

NOTE This is not a [DUPLICATE] of the other question since it addresses the unexpected behavior of bash where a failure of the subprocess to start, say due to 'command not found', can leave $! set to the pid of the calling process instead of the pid of the failed subprocess or "" (null). -- docsalvager

I'm writing a bash 4.1 script...

  • Would like to run another script (or internal function) in the background...
  • And capture the pid of that background job in a variable in the foreground script...
  • If the job doesn't start, the variable should get a null (i.e. "").

Either of these work if the backround job always starts...

cmd &
VAR=$!

or...

( cmd & ); VAR=$!

If cmd runs, the pid of the last command started ($!) will be the one we want and $VAR will be correct.

However, if cmd doesn't start, $VAR will the the pid of the last command before that that started which could be the pid of the foreground script itself. Since I need the pid of cmd for the purpose of killing the process under certain conditions later on, possibly getting the wrong pid just won't do.

What I really need is for VAR to only be changed if the background job ran. Something like the syntactically incorrect...

VAR=
( cmd & );  && VAR=$!

Implemented Solution

The comments and Joseph R's answer have been very helpful. For the benefit of anyone with a similar problem...

The following bash function runs the GtkDialog standby script in the background. Being inside a function, which itself is run in a subprocess, seems to provide the isolation I need to avoid getting the PID of the calling script instead of the PID of the standby script if standby fails...

function standbyBg () {
  ifHelpShow "$1" 'standbyBg
    Run standby (busy "throbber") in background and echo its pid.
    Example:
        standbyPID=$(standbyBg)
    Revised:
        20131123 docsalvager'  && return
  #
  standby  >/dev/null 2>&1  &
  echo $!
}
DocSalvager
  • 2,152
  • Have you considered the possibility of checking $? before assigning from $!? I have no idea if it'll work, but if it does then it might help with only assigning VAR if the command invocation is successful. – user Sep 11 '13 at 13:41
  • 3
    @enzotib Not really. The OP knows about using $! to capture the last command's PID. They want to make sure the backgrounded command was actually started so that $! doesn't capture the PID of an earlier command. – Joseph R. Sep 11 '13 at 19:04
  • What do you mean by “if the backround job always starts”? Unless fork fails due to a lack of resources, the background job always starts in your code snippet. Even if the job consists solely of some external command that does not start, the job is still started and $! is set. Please post your actual problem, including steps to reproduce it. P.S. I voted to close as “unclear”: this isn't really a duplicate of http://unix.stackexchange.com/q/30370, since you know about $!, but your question doesn't make sense: $! is the answer. – Gilles 'SO- stop being evil' Sep 11 '13 at 22:58
  • Thanks. You're right in part. Sorry for forgetting this was still hanging without a resolution. I've added the solution implemented to the bottom of the question. – DocSalvager Jan 16 '14 at 10:56
  • A rather complex solution: export PID_FILE=$(mktemp /tmp/standby.XXXXXXXXXXXXXXX); (tfile=$PID_FILE; ./standby& s_pid=$!; echo $s_pid > $tfile; wait; rm $tfile;)& sleep 0.1; if [ -r $PID_FILE ] ; then S_PID=$(cat $PID_FILE); else S_PID=""; fi ; echo pid $S_PID; unset PID_FILE – gam3 Sep 04 '16 at 07:42
  • 2
    This is definitely not a duplicate. Please remove the "duplicate" tag. While part of the answer overlaps with the answer to that other question, the question is very different. I would never have found the answer if this separate question didn't exist here. – Yitz Jun 03 '19 at 06:42

1 Answers1

8

A bit of a hackish workaround:

jobs &>/dev/null
cmd &
new_job_started="$(jobs -n)"
if [ -n "$new_job_started" ];then
    VAR=$!
else
    VAR=
fi

Thanks to @terdon's comment for directing my attention to help jobs.

Joseph R.
  • 39,549
  • 2
    Nice hack, +1. If you just run jobs -p | wc -l once before launching cmd and then once more afterwards and compare the numbers, you can do it even if there are other background jobs. – terdon Sep 11 '13 at 13:49
  • 1
    Thanks for directing my attention to help jobs. I got an even easier solution. Editing answer... – Joseph R. Sep 11 '13 at 13:52