Bug 34811 - sh(1) "jobs" is not pipeable
Summary: sh(1) "jobs" is not pipeable
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: 4.3-STABLE
Hardware: Any Any
: Normal Affects Only Me
Assignee: Jilles Tjoelker
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2002-02-11 00:30 UTC by Slaven Rezic
Modified: 2025-11-14 21:58 UTC (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Slaven Rezic 2002-02-11 00:30:01 UTC
	
	The output of /bin/sh's "jobs" builtin cannot be sent to a
	pipe.

How-To-Repeat: 	
	Start a process in a sh in the background.
	Type:

		jobs | grep .

	There is no output. But:

		jobs > /tmp/foo

	will output the jobs into /tmp/foo.
Comment 1 Martin Kaeske 2002-02-14 12:18:18 UTC
I can't say I understood /usr/src/bin/sh completely, but it seems
that sh forks a new shell if it detects a pipe "|". This new shell
is responsible for evaluating the pipe, that means 
"jobs|grep" creates a new shell and jobs is executed within
the new shell and not in root-shell. I don't know wether such
behaviour is intended or not.
What disturbs me is the following (from sh(1)):
  Shell builtin commands are the exception to this rule. They are
  executed in the current shell,...
Does "current shell" mean the root-shell? If that's correct I think
sh doesn't behave as documented in sh(1).

If its really a bug can someone tell me what the expected behaviour is?
Maybe I can fix it. Or is such treatment of pipes required by any
standard (maybe posix-1003.2)?.

Martin
Comment 2 Kero-Chan 2004-10-02 03:53:31 UTC
The standard behaviour is this:

   faber $ (sleep 10 & jobs | cat)
   [1]+  Running                 sleep 10 &
   faber $ (sleep 10 & jobs | grep '.')
   [1]+  Running                 sleep 10 &

Commands inside () are forced into a subshell;
sleep 10 is then started in the background;
jobs is a shell builtin, but it's output must still be pipable;
AND IT IS!

Does the sequence "(sleep 10 & jobs | cat)" produce output at you?
[It should.]
Comment 3 Jilles Tjoelker freebsd_committer freebsd_triage 2009-04-03 20:34:18 UTC
The way I read POSIX, I think this does not have to work. Commands in a
pipeline with two or more commands may or may not be executed in a
subshell (this 'or' applies to each of the commands individually), and a
subshell has its own list of unwaited jobs.

Accordingly,  (jobs)  gives empty output (in /bin/sh as well as bash4).
bash4 seems to do special trickery with the jobs command in pipelines
(as permitted): even in commands like  jobs >`tty` | jobs  both give
proper output. This, however, breaks down if you make it more
complicated such as by replacing  jobs  with  { jobs; }.

On the other hand, $(jobs) works in both /bin/sh and bash4, although
POSIX says that command substitution happens in a subshell. However,
POSIX also mentions $(jobs -p) in an informative section, and it seems
useful.

If you want to add this feature, evalpipe() in eval.c would probably be
the place. It would be quite specific to very few builtin commands which
tend not to be used much in scripts. (Note that slow operations in the
main shell process cannot be ^Z'ed, and that builtin commands may be
overridden with functions.)

-- 
Jilles Tjoelker
Comment 4 Remko Lodder freebsd_committer freebsd_triage 2011-01-28 08:23:41 UTC
Responsible Changed
From-To: freebsd-bugs->jilles

Hi Jilles, can you have a look at the PR and close it or do whatever is 
needed with it? Thanks!
Comment 5 Jilles Tjoelker freebsd_committer freebsd_triage 2011-01-28 10:59:10 UTC
> [request to allow jobs | ...]

This could be allowed in a way similar to optimized command
substitution. If here-documents were expanded in the main shell
environment, something like

grep foo <<EOF
$(jobs)
EOF

would perform the requested function of

jobs | grep foo

This change to here-documents may not be made but suggests a way to
implement this in the code.

Apart from jobs, the change may also be useful to improve performance
with echo and printf, saving a fork in constructions like

x=$(echo "$x" | sed -e 's/#/%/')

That would need some care to preserve NUL bytes and final newlines in
echo/printf output.

Various other caveats are in my followup of 3 Apr 2009.

In any case, I consider this low priority as POSIX does not require it
to work and you can work around it by placing jobs in a command
substitution.

-- 
Jilles Tjoelker
Comment 6 Jilles Tjoelker freebsd_committer freebsd_triage 2015-08-12 20:56:03 UTC
NetBSD sh solves this differently. In a subshell, the jobs builtin shows the parent's jobs until a fork occurs.

For consistency, "until a fork occurs" should be changed to "until a background command is started", so it does not depend on whatever happens to be a builtin or is executed with or without forking.

This will affect usage of "jobs" at many locations, so I'm not really sure about it. Someone might expect the output of "jobs" to be empty.

The extra code is fairly small, so that's not much of an issue.