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