[Last Call] Learn how to a build a cloud-first strategyRegister Now

  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 297
  • Last Modified:

Checking jobs in a shell script.

I'm trying to use jobs builtin to see which of the jobs that my shellscript spawned have exited.  But it just doesn't seem to work.  For example, consider the following:

hjstein@blinky:~$ cat jobtest

tail -f ~/.bashrc >/dev/null &
hjstein@blinky:~$ ./jobtest
hjstein@blinky:~$ ps -aux | grep tail
hjstein  21272  1.0  0.4   880   316  ?  S   13:12   0:00 tail -f /home/hjstein
hjstein  21274  0.0  0.5   972   344  ?  S   13:12   0:00 grep tail

The tail is running, but jobs doesn't report anything.  WHY!?!?!?!

Also, how do I get around it?

This is on linux with bash.  zsh behaves the same way.

1 Solution
tail is running on your initialisation file which means that it will periodically display the output of that file. jobs is running in a different shell so tail will not display its output.
hj2Author Commented:
jobs is run by the same shell which ran tail, as is evident by running the same 2 commands by hand:

   hjstein@blinky:~$ tail -f ~/.bashrc >/dev/null & 
   [1] 433
   hjstein@blinky:~$ jobs
   [1]+  Running                 tail -f ~/.bashrc >/dev/null &

This is also evident by sourcing jobtest instead of running it:

   hjstein@blinky:~$ . jobtest
   [1]+  Running                 tail -f ~/.bashrc >/dev/null &
   hjstein@blinky:~$ . jobtest
   [1]-  Running                 tail -f ~/.bashrc >/dev/null &
   [2]+  Running                 tail -f ~/.bashrc >/dev/null &
   hjstein@blinky:~$ . jobtest
   [1]   Running                 tail -f ~/.bashrc >/dev/null &
   [2]-  Running                 tail -f ~/.bashrc >/dev/null &
   [3]+  Running                 tail -f ~/.bashrc >/dev/null &

The only difference btw ". jobtest" and "./jobtest" is that the former is run by the current shell, and the latter is run in a subshell.

The reason is that in bash scripts background jobs are childs
of init (PID = 1), while in an interactive bash they are a child
of the bash itself. So the jobs command cannot find them because
they are in a different program group.
See man bash for a rough description about JOB CONTROL, for more
details I think you have to step deeper into the sources of bash.

Your script will work with csh (and probably with tcsh also).
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

hj2Author Commented:
ahoffmann writes:

   The reason is that in bash scripts background jobs are childs
   of init (PID = 1), while in an interactive bash they are a
   child of the bash itself.

Why is this?  I didn't notice any mention of this in the man page.  Do you have any reference for this (other than the source code)?

hj2Author Commented:
I confirmed your answer with ps -l, so I'm ready to give you (ahoffmann) an A rating, but I'd still like to know:

a) why processes spawned by noninteractive shells are children of init instead of being children of the shell that spawned them, and

b) Is there any way around this under bourne shells (bash, sh, ksh, ...)?

If you don't know, then just say so, and I'll close the question.
As I said, please refer to man bash for the "program group"
That there is a difference for script and interactive, I also
found by testing ;-) so that's the reason I said you must look
at the sources for the hard details.

Why it is different for bash and sh? I don't know. But bash and
sh behave the same (ksh probably too) 'cause sh is the ancestor
of all those shells, while csh was a complete new approach.
hj2Author Commented:
Ok.  I'm satisifed.  I finally found "set -m" in the man page, which infers that job control is on by default for interactive shells and off otherwise.  The section on job control infers that subprocesses will be children of the current shell when job control is on and will be children of init when job control is off.

Thanks for your help.

I am just curious here.  But on my host, HPUX10.10, both ksh and sh do not behave that way.  The background jobs are not owned by init, they are owned by the shell.  Here is what I get:

/user3/seedy/tmp> echo $0

/user3/seedy/tmp> cat ./jobtst
tail -f ~/.kshrc >/dev/null &
ps -l -u seedy
jobs  -l

/user3/seedy/tmp> ./jobtst
  1 R  1026 15225 15072  0 178 20  18d1980  108        - ttyp4     0:00 sh
  1 R  1026 15227 15225  3 178 20  175ba80   23        - ttyp4     0:00 ps
  1 S  1026 15226 15225  2 168 20  203c480    8 7ffe6000 ttyp4     0:00 tail
  1 S  1026 15072 14999  0 158 20  17ad980  107   b687c0 ttyp4     0:00 sh
[1] + 15226      Running                 <job name not known>

As you can see tail's ppid is not 1.  Ksh also behaves this way.

hj2, if you do a ps -l outside the jobtst script, the tail will
be owned by pid 1 - this because the shell that was executing jobtst has terminated and hence the tail process is inherited by init.
hj2Author Commented:
Good observation.  I don't understand this process group business enough to figure out what's going on...

Seedy, you're right: processes spawned by a shell get PID 1 if
their parrent dies.

hj2,  set -m   explains the things about the process groups
So my answer was right half the way, thanks for the points anyway

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now