[Pdx-pm] job control

forehead nforrett at wgz.com
Mon Jun 2 14:09:30 CDT 2003

On Mon, 2 Jun 2003, Austin Schutz wrote:

> On Sun, Jun 01, 2003 at 06:15:33PM -0700, Randall Hansen wrote:
> > Folks ~
> > 
> > I'm trying to get a list of bash's suspended jobs using Perl.  I
> > initially thought this would be trivial (e.g. perl -e "print exec
> > jobs"), but I've yet to succeed.  I tried permutations of "system,"
> > "exec," backticks, etc.  The closest I've come is:	$ perl -e "print
> > `jobs`" which apparently does an eval on the output of jobs (causing
> > errors and yielding nothing useful).  But what I really want is to
> > capture and examine the output, not echo it.
> > 
> > It occurs to me (after seeing "No such file ..." a few times) that
> > technically jobs is a builtin of bash, not a system command.  This
> > hasn't gotten me any closer.  I can start a new bash with perl, but
> > can't figure out how to send builtins to an existing one.
> > 
> > Which in turn makes me think that this may be more of a bash question
> > than a Perl one, but I thought I'd ask anyway.

Sorry in advance, but most of this response deals with the *nix C interface
for jobs control. The point is to illustrate why asking bash is not going to
provide an answer. If you have particular averstions to C, consider this your
warning. =)

You can't spawn a subshell and get the suspended jobs the shell that
forked/execed perl. This completely rules out system, backticks and pretty
much every option you've tried so far. E.g.

bash$ cat
bash$ jobs
[1]+  Stopped                 cat
bash$ bash
bash_subshell$ jobs
bash_subshell$ ^D
bash$ jobs
[1]+  Stopped                 cat

I tweaked the output to clarify which prompts were in the parent shell and
which were in the child shell. Here's a teeny bit of info on how job control
is working:

1. bash forks/exec a child
2. bash calls member of wait family. This blocks until either the child exits,
   _or_ the child recieves a signal.
3. While the child is running, the terminal driver intercepts the ^Z and 
   rather than passing it along, it sends a SIGTSTP (terminal keyboard stop).
4. This causes the blocking wait call in the bash process to return and 
   indicates to bash that its child recieved SIGTSTP. Bash obliges by printing
   a prompt for you and adds the relevent info to some internal list of 
   suspended processes.
5. When you type "fg" in bash, it takes the appropriate process off the 
   suspend list and sends it a SIGCONT.
6. bash then proceeds to call its favorite flavor of wait.

That is enough info to give you the gist of what's going on (some details were
glossed over for the sake of brevity). The set of suspended processes is held
internally by bash. As far as I'm aware, there is no way to get this
information out programatically from a child process (in perl, or otherwise).  
You can't use a sub-shell because the parent has the list, and the only way
you can get your parent shell to run again is by suspending yourself.

You best option is to get a process tree from the OS (via ps, /proc, or
otherwise) and filter out the bits you do or do not need. Both ps and /proc
are _very_ unportable, but I don't have a better anwser for you (I'm not even
aware of a standard C interface to get this information off the top of my
head, though Advanced Programming in the UNIX Environment" by Richard Stevens
may be a place to look).

PS- You may notice that if the application puts the terminal into raw mode, ^Z
is not interpreted by the terminal driver code. This is why vim doesn't
suspend when you press ^Z in insert mode (pressing ^Z in command mode will
suspend vim, but that is only because Vim interpreted the ^Z and specifically
asked to have itself suspended).

-- Nick

More information about the Pdx-pm-list mailing list