jobs command in dash doesn't work with pipes in scripts

Bug #243406 reported by Lee Bradshaw
8
This bug affects 1 person
Affects Status Importance Assigned to Milestone
dash (Ubuntu)
New
Undecided
Unassigned

Bug Description

Binary package hint: dash

I have a script that uses "jobs | grep -c Running" in some nested loops to limit the number of parallel jobs that are running. When /bin/sh is set to dash, the script fails. When /bin/sh is set to bash the script works as desired. I can change the first line of the script to explicitly call /bin/bash instead of /bin/sh, but maybe dash should be changed as well. Here are two test scripts that demonstrate the problem:

dagger ~/job_count $ cat job_count.bash | sed -e "s/^/ /"
    #!/bin/bash

    sleep 10&
    sleep 10&

    printf "job list\n"
    jobs
    printf "jobcount = "
    jobs | wc -l
    printf "job list (piped through cat)\n"
    jobs | cat
    printf "job list\n"
    jobs

    wait
    echo finished

dagger ~/job_count $ cat job_count.dash | sed -e "s/^/ /"
    #!/bin/dash

    sleep 10&
    sleep 10&

    printf "job list\n"
    jobs
    printf "jobcount = "
    jobs | wc -l
    printf "job list (piped through cat)\n"
    jobs | cat
    printf "job list\n"
    jobs

    wait
    echo finished

Here's the output from the bash script. Notice jobcount=2 and the jobs output appears when piped through cat.

dagger ~/job_count $ ./job_count.bash | sed -e "s/^/ /"
    job list
    [1]- Running sleep 10 &
    [2]+ Running sleep 10 &
    jobcount = 2
    job list (piped through cat)
    [1]- Running sleep 10 &
    [2]+ Running sleep 10 &
    job list
    [1]- Running sleep 10 &
    [2]+ Running sleep 10 &
    finished

Here's the output from the dash script. Notice jobcount=0 and the jobs output does not get piped through cat.

dagger ~/job_count $ ./job_count.dash | sed -e "s/^/ /"
    job list
    [2] + Running
    [1] - Running
    jobcount = 0
    job list (piped through cat)
    job list
    [2] + Running
    [1] - Running
    finished

I would expect the output from the bash and dash scripts to be the same. Specifically the dash script should print "jobcount = 2" and should list the two running jobs immediately after "job list (piped through cat)".

Revision history for this message
Lee Bradshaw (lee-sectioniv) wrote :

I'm using a clean install of ubuntu 8.04.

dagger ~/job_count $ dpkg -s dash|grep Version
Version: 0.5.4-8ubuntu1

Revision history for this message
JM Williams (jmdwilliams) wrote :

I think that the problem here is that Dash is running “jobs | wc -l” and
“jobs | cat” in subshells. These subshells have no stopped jobs or running
background jobs to report, which is why the output of jobs is empty.

If in Dash you run:

    sleep 10 &
    jobs

you will get the output you are expecting from jobs, but if you run:

    sleep 10 &
    (jobs)

(which puts jobs in a subshell), you will get no output.

The Single Unix Specification states,¹ “Additionally, each command of a
multi-command pipeline is in a subshell environment; as an extension, however,
any or all commands in a pipeline may be executed in the current environment.”
This shows that merely putting jobs into a pipeline *could* cause it to be in
a subshell.

The Dash man page states, “Note that unlike some other shells, each process in
the pipeline is a child of the invoking shell (unless it is a shell builtin,
in which case it executes in the current shell -- but any effect it has on the
environment is wiped).” As jobs is a shell builtin (so says “type jobs”), it
ought to execute in the current shell, and so shouldn’t it report stopped and
background jobs of the shell containing the pipeline? I’m not sure, so I’m
going to leave this bug open for someone else to decide.

However, if you want to make your shell script POSIX-compliant and therefore
portable, then you presumably shouldn’t rely on the behaviour that you were
expecting. I don’t know what the best workaround here would be, but *one*
workaround would be to use “>”-redirection to write from jobs to a temporary
file which you could then grep and line-count: this alone wouldn’t create a
multi-command pipeline.

In any case, I wouldn’t expect Bash in its default mode necessarily to behave
the same as Dash, which aims to be POSIX compliant. Bash when started with
the option --posix I *might* expect to behave the same as Dash. In this case,
it doesn’t, however: Bash gives the same output if you change the first line
of your Bash script to “#!/bin/bash --posix”.

I hope some of the above helps!

    1. http://www.opengroup.org/onlinepubs/000095399/utilities/xcu_chap02.html#tag_02_12

Revision history for this message
Lee Bradshaw (lee-sectioniv) wrote :

Thanks for the analysis. I'll consider the workaround you proposed and see if I can think of any other options.

I believe running the shell builtin jobs in a subshell is a bug. Running jobs in a new shell will never return any job information.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.