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.
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
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.]
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
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!
> [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
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.