On Wed, 12 Jul 2023, Lucas de Sena wrote:
> In OpenBSD's ksh(1), a background process running in a subshell in a
> pipeline does not read from standard input.

AFAICT, there's a bug in the OpenBSD shell, but not the one you're 
reporting.  :/

The behavior you're seeing is correct per POSIX...for sh(1); if someone 
wants to make ksh(1) behave differently on this point I suppose that's 
possible, but it makes more sense to me to write scripts that don't depend 
on a non-POSIX behavior.  I'll get the bug that does exist later.


Let's follow the POSIX dots:

> Try running the following:
> 
>       $ ksh -c 'echo foo | { cat & }'
>
> The background cat(1) should read "foo" and write it into standard
> output, but it does not.

Quoting from POSIX 2016 XCU:

------------------
2.9.3.1 Asynchronous Lists
    If a command is terminated by the control operator <ampersand> ('&'), the 
    shell shall execute the command asynchronously in a subshell. This means 
    that the shell shall not wait for the command to finish before executing 
    the next command.

    The format for running a command in the background is:
    command1 & [command2 & ... ]

    If job control is disabled (see set, −m), the standard input for an 
    asynchronous list, before any | explicit redirections are performed, shall 
    be considered to be assigned to a file that has the same properties as 
    /dev/null. This shall not happen if job control is enabled. In all cases, 
    explicit | redirection of standard input shall override this activity.
------------------

Okay, so if no job control, then "cat &" is implicitly "cat </dev/null &". 
So, is job control enabled for this?

------------------
    −m
        This option shall be supported if the implementation supports
        the User Portability Utilities option. All jobs shall be
        run in their own process groups. Immediately before the
        shell issues a prompt after completion of the background
        job, a message reporting the exit status of the background
        job shall be written to standard error. If a foreground job
        stops, the shell shall write a message to standard error
        to that effect, formatted as described by the jobs utility.
        In addition, if a job changes status other than exiting
        (for example, if it stops for input or output or is stopped
        by a SIGSTOP signal), the shell shall write a similar message
        immediately prior to writing the next prompt. This option
        is enabled by default for interactive shells.
------------------

Since -m wasn't specified directly, it would only be on if the shell is 
interactive.  Is this shell interactive?

------------------
    If the −i option is present, or if there are no operands and the 
    shell’s standard input and standard error are attached to a terminal,
    the shell is considered to be interactive.
------------------

In POSIX, the -c option does not have an option argument, but rather just 
specifies that at least one positional argument is required and the first 
positional argument is the command to be invoked.  That makes the command 
an *operand*, not an option argument:

------------------
NAME
    sh — shell, the standard command language interpreter
SYNOPSIS
    sh [−abCefhimnuvx] [−o option]... [+abCefhimnuvx] [+o option]... 
        [command_file [argument...]]
    sh −c [−abCefhimnuvx] [−o option]... [+abCefhimnuvx] [+o option]... 
        command_string [command_name [argument...]]
<...>
OPTIONS
<...>
    The following additional options shall be supported:
    -c        Read commands from the command_string operand. Set the value 
              of special  parameter 0 (see Section 2.5.2, on page
              2350) from the value of the command_name operand and
              the positional parameters ($1, $2, and so on) in
              sequence from the remaining argument operands. No
              commands shall be read from the standard input.
------------------

This invocation doesn't have the -i option but *does* have an operand, so 
it is _not_ considered interactive.  Ergo job control is not enabled by 
default and therefore "cat &" is implicitly redirected from /dev/null.  
The behavior you're seeing is correct per POSIX.

Note that this behavior in bash seems wrong:

$ bash --posix -c 'echo before=$-; echo foo_first=$- | { echo second=$-; cat & 
tr o e ; }'
before=hBc
second=hBc
foo_first=hBc
$

No -m option in 'second' and yet cat read from stdin instead of /dev/null.


So, I said there's a bug in the OpenBSD shell: sh/ksh apparently turn off 
the -i and -m options in pipelines, but I see nothing that permits that 
behavior.  This means setting the -i or -m options at the top-level 
doesn't propagate to the tail of the pipe; you would have to set one in 
the tail:

$ sh -ic 'echo before=$-; echo foo_first=$- | { echo second=$-; cat & tr o e ; 
}'
before=cim
second=c
fee_first=c
$ sh -ic 'echo before=$-; echo foo_first=$- | { set -m; echo second=$-; cat & 
tr o e ; }'
before=cim
second=cm
foo_first=c
$

Contrast with bash:
$ bash -ic 'echo before=$-; echo foo_first=$- | { echo second=$-; cat & tr o e 
; }'
before=himBHc
second=himBHc
foo_first=himBHc
$


Philip Guenther

Reply via email to