[comp.unix.questions] C-shell idiosyncracy ??

vipin%samsun@Sun.COM (Vipin Samar) (11/10/87)

It seems that I can redirect the output of a C-shell command
to a file, but I cannot pipe it to some other command. Logically,
this should not have happened. Anyway, the following happens
for C-shell as well as for korn-shell.

{samsun} jobs
[1]  + Stopped		emacs foo
{samsun} jobs > bar
{samsun} cat bar
[1]  + Stopped		emacs foo
{samsun} jobs | more
{samsun}

So, for some reasons I just cannot pipe the output of "jobs".
I am just wondering how was this bug (feature !) implemented. ?




vipin@sun.com || sun!vipin || (415) 691-6694 (o) || (408) 749-0259 (r)

decot@hpisod2.HP.COM (Dave Decot) (11/11/87)

> It seems that I can redirect the output of a C-shell command
> to a file, but I cannot pipe it to some other command. Logically,
> this should not have happened. Anyway, the following happens
> for C-shell as well as for korn-shell.
> 
> {samsun} jobs
> [1]  + Stopped		emacs foo
> {samsun} jobs > bar
> {samsun} cat bar
> [1]  + Stopped		emacs foo
> {samsun} jobs | more
> {samsun}
> 
> So, for some reasons I just cannot pipe the output of "jobs".
> I am just wondering how was this bug (feature !) implemented. ?

This works in HP-UX's /bin/csh and in its /bin/ksh, i.e.,
"jobs | more" prints:

    [1]  + Stopped		emacs foo

Dave Decot
hpda!decot

chris@mimsy.UUCP (Chris Torek) (11/12/87)

In article <33414@sun.uucp> vipin%samsun@Sun.COM (Vipin Samar) writes:
>{samsun} jobs
>[1]  + Stopped		emacs foo
>{samsun} jobs > bar
>{samsun} cat bar
>[1]  + Stopped		emacs foo
>{samsun} jobs | more
>{samsun}
>
>So, for some reasons I just cannot pipe the output of "jobs".

To see why this happens, consider how pipes are implemented.
After creating a pipe, it is necessary to fork.  A fork is
not the parent of any of its parent's processes, hence a
forked sub-C-shell must forget all its parent's jobs.

Hence, `jobs | cat' or `(jobs)' or anything similar produces
no output.  `alias | cat' works fine since a subshell need not
forget about the parent's aliases.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

vic@phoenix.Princeton.EDU (V Duchouni) (11/13/87)

In article <9316@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>In article <33414@sun.uucp> vipin%samsun@Sun.COM (Vipin Samar) writes:
>>{samsun} jobs
>>[1]  + Stopped		emacs foo
>>{samsun} jobs > bar
>>{samsun} cat bar
>>[1]  + Stopped		emacs foo
>>{samsun} jobs | more
>>{samsun}
>>
>>So, for some reasons I just cannot pipe the output of "jobs".
>
>To see why this happens, consider how pipes are implemented.
>After creating a pipe, it is necessary to fork.  A fork is
>not the parent of any of its parent's processes, hence a
>forked sub-C-shell must forget all its parent's jobs.

     Well, it is not strictly necessary (but very advisable) to fork,
     that is for the command:

machine% built_in | binary

   one migh hope that the shell would fork only once, spawning
   "binary" and execute built_in followed by a wait() , with output to the pipe,
   (potentially stopping if the pipe fills up, ouch!!! One would
   find the shell dead on every instance of a wrong or buggy filter ) in this
   case "jobs | cat" would work. 

   Instead the shell also (v?)forks (as it would for "binary1 | binary2")
   and the child (csh) ( which gets a copy of all internal variables,
   hence "alias | cat" as above) executes the built in and exits,
   while the parent waits for the exit of "binary". Here if
   binary is stupid and refuses to read, you will get impatient
   type ^C, and bingo "binary" dies, and the parent takes control.

   It looks like the shell needs some help from the kernel in
   determining the number of current jobs that are still alive
   ( remember if you typed jobs, and the shell was running
   it cannot have been waiting for its children and they may have
   died in the interim ), so while the shell knows the pids and 
   job numbers of its children ( and hence so does the child executing
   "jobs" in "jobs | cat" ) the child seeks current info on the jobs
   IT has spawned and finds none!

   This is perhaps disappointing, but it avoids disaster.

 V. Duchovni: email:		vic@fine.princeton.edu

gwyn@brl-smoke.ARPA (Doug Gwyn ) (11/13/87)

In article <33414@sun.uucp> vipin%samsun@Sun.COM (Vipin Samar) writes:
>{samsun} jobs
>[1]  + Stopped		emacs foo
>{samsun} jobs | more
>{samsun}

"jobs | more" is a pipeline, so it is run as a subprocess,
and in the subprocess there are no stopped jobs (they belong
to the parent shell process).

katzung@laidbak.UUCP (Brian Katzung) (11/14/87)

In article <9316@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>In article <33414@sun.uucp> vipin%samsun@Sun.COM (Vipin Samar) writes:
...
>>So, for some reasons I just cannot pipe the output of "jobs".
>To see why this happens, consider how pipes are implemented.
>After creating a pipe, it is necessary to fork.

It is necessary to fork to create the read end of the pipe, but
not for the write end of the pipe (and the write end of the pipe
is the supplier of jobs information).  It is just as easy to
write to the file descriptor for a pipe as it is to write to
the file descriptor for a file.  His shell and the several I just
tested are broken because they chose to take the lazy approach by
forking instead of juggling file descriptors in the current shell.
IT CAN BE DONE RIGHT, and from the responses, I gather the HPUX
people did do it right.

Actually, in a degenerate case, you could pipe from one built-in
to another, as long as you didn't ever send more than 4K before
you read it (ie, "history -hr 8 | source -h" would append your
last 8 events in reverse order onto your history list if source
accepted input from the standard input).
-- Brian Katzung   ihnp4!laidbak!katzung

kyle@xanth.UUCP (Kyle Jones) (11/15/87)

In article <9316@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes:
> In article <33414@sun.uucp> vipin%samsun@Sun.COM (Vipin Samar) writes:
> >[1]  + Stopped		emacs foo
> >{samsun} jobs | more
> >{samsun}
> >
> >So, for some reasons I just cannot pipe the output of "jobs".
> 
> To see why this happens, consider how pipes are implemented.
> After creating a pipe, it is necessary to fork.  A fork is
> not the parent of any of its parent's processes, hence a
> forked sub-C-shell must forget all its parent's jobs.

No, it means the forked child C-shell cannot wait() for them.  The
internal information that csh stores about process states is available
to the child, so why couldn't this information be used?  It would be
wrong in some instances (e.g. a process exits in the meantime) but the
parent C-shell will notice and print updated information at the next
prompt (or immediately if notify is set).

kyle jones  <kyle@odu.edu>  old dominion university, norfolk, va  usa

milgr%brandeis.csnet@RELAY.CS.NET (Marc Milgram) (11/16/87)

Subject: Re: C-shell idiosyncracy ??

> It seems that I can redirect the output of a C-shell command
> to a file, but I cannot pipe it to some other command. Logically,
> this should not have happened. Anyway, the following happens
> for C-shell as well as for korn-shell.
> 
> {samsun} jobs
> [1]  + Stopped		emacs foo
> {samsun} jobs > bar
In this case, csh redirects its own output, then finds what jobs it is
running.

But:
> {samsun} cat bar
> [1]  + Stopped		emacs foo
> {samsun} jobs | more
> {samsun}
>
In this case, csh finds there is a pipe (|) in the command, so it
spawns off a csh which finds that it is not running any subprocessies.


	-Marc Milgram
	 milgr@brandeis.edu.cs.net

chris@mimsy.UUCP (Chris Torek) (11/16/87)

As several people have pointed out, it is possible (not necessarily
easy) to have builtin commands run as the source or sink of a
pipeline (although at most one such source or sink).  The question
in <33414@sun.uucp>, however, was `why does this happen'; that was
what I answered.

Actually, to be even more specific, csh forks the head ends of all
pipes.  The tail end of a pipe will be done directly by csh if the
command is a csh builtin.  Contrast this with sh, which forks the
tail ends of pipes first:

	a | b | c | d | e &

In csh, you might get

	% jobs
	[1]	714 Runnning		a |
		715			b |
		716			c |
		717			d |
		718			e

In sh, had it a jobs command, these might be listed as

	718 Running	a |
	717		b |
	716		c |
	715		d |
	714		e

In particular, in csh, builtins are run directly by the shell when
they are at the tail end of pipes.  This is the most straightforward
implementation, given the direction of creation shown above.  Try,
for instance,

	% jobs | cat

and

	% cat /dev/null | jobs

The former produces no output; the latter lists some jobs, then
prints

	Reset tty pgrp from xxx to yyy

---which just goes to show that there are bugs in csh (what a
revelation :-) !).  Indeed, it is necessary to do another `jobs'
command to flush the `cat | jobs' job from csh's joblist.  Also
incidentally, this particular bug would wreak havoc with tty signals
(interrupt, stop) if any of csh's builtins were long-running.

Were csh more clever, it could indeed either avoid forking the jobs
command in `jobs | more' (but at least one `jobs' in `jobs | jobs |
more' must be forked), or defer forgetting about its parent's jobs.
Csh is just not that clever.

Having once again demonstrated that I can write more exposition
on a smaller subject than anyone else :-), I now return you to
your regular netnews,
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris