Re: Surprising behaviour when reading from a named pipe and standard input

Harald van Dijk
2026-02-10T02:08:47Z

On 10/02/2026 01:11, Earnestly wrote:
> I came across this behavioural difference between dash and sh(bash),
> bash and zsh.
>
> When attempting to read from a named pipe and stdin at the same time in
> a backgrounded process I appear to be losing stdin:
>
> #!/bin/dash --
>
> f() {
> mkfifo pipe
>
> cat pipe - <&0 &
In both bash and dash, backgound commands have an implicit </dev/null
redirection unless an explicit redirection is provided, but they
disagree on exactly how that works.

In bash, <&0 suppresses the implicit </dev/null, meaning the outer
environment's stdin gets used.

In dash, internally, <&0 is a no-op because redirecting stdin to itself
shouldn't change anything. Because it is a no-op, it does not suppress
the implicit </dev/null.

In dash, effectively, it is as if <&0 overrides rather than suppresses
the implicit </dev/null: the implict </dev/null is performed, and then
<&0 overrides that but at that point fd 0 is already /dev/null, so after
that, it is still /dev/null.

I have mixed feelings on which behaviour makes more sense, but for
better or worse this is something that portable shell scripts just need
to avoid. You can make this work by temporarily duplicating stdin to a
different fd and then back:

{ cat pipe - <&3 & } 3<&0

Cheers,
Harald van Dijk