* Surprising behaviour when reading from a named pipe and standard input
@ 2026-02-10 1:11 Earnestly
2026-02-10 2:00 ` Harald van Dijk
0 siblings, 1 reply; 3+ messages in thread
From: Earnestly @ 2026-02-10 1:11 UTC (permalink / raw)
To: dash
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 &
echo hello > pipe
rm -f pipe
wait
}
echo hi | f
This outputs (busybox sh likewise):
hello
With sh(bash), bash and zsh I get (and expect):
hello
hi
At first I thought it might be a difference in how dash implements
set -m due to how jobs without it set have their stdin opened as
/dev/null but adding set -m does not appear to help.
Can you help me understand what is happening and why dash differs?
---
I would like to use this technique with age to read identities from a
pipe as the tool doesn't offer another way, read code below:
decrypt() {
# XXX https://github.com/FiloSottile/age/discussions/685
tmpdir=${TMPDIR:-/tmp}/curator-$$
pipe=$tmpdir/s
defer rm -rf -- "$tmpdir"
nofail mkdir -m 0700 -- "$tmpdir"
# XXX Using a pipe limits the size of the identity to around 1MiB on Linux.
# cf. /proc/sys/fs/pipe-max-size
nofail mkfifo -m 0600 -- "$pipe"
# nb. stdin needs to be specified as without job control (set -m) the
# command will have its stdin set to /dev/null.
age -i "$pipe" -d <&0 &
exec 9>"$pipe"
rm -f -- "$pipe"
key_request private >&9
exec 9>&-
wait
}
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: Surprising behaviour when reading from a named pipe and standard input
2026-02-10 1:11 Surprising behaviour when reading from a named pipe and standard input Earnestly
@ 2026-02-10 2:00 ` Harald van Dijk
2026-02-10 10:24 ` Earnestly
0 siblings, 1 reply; 3+ messages in thread
From: Harald van Dijk @ 2026-02-10 2:00 UTC (permalink / raw)
To: Earnestly, dash
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
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: Surprising behaviour when reading from a named pipe and standard input
2026-02-10 2:00 ` Harald van Dijk
@ 2026-02-10 10:24 ` Earnestly
0 siblings, 0 replies; 3+ messages in thread
From: Earnestly @ 2026-02-10 10:24 UTC (permalink / raw)
To: dash
On Tue, Feb 10, 2026 at 02:00:18AM +0000, Harald van Dijk wrote:
> In bash, <&0 suppresses the implicit </dev/null, meaning the outer
> environment's stdin gets used.
I hoped that dash would do the same, when faced with an explicit use
of <&0, which I try to base on my reading of the standard:
> If, and only if, job control is disabled, the standard input for the
> subshell in which an asynchronous AND-OR list is executed shall
> initially be assigned to an open file description that behaves as if
> /dev/null had been opened for reading only. This initial assignment
> shall be overridden by any explicit redirection of standard input
> within the AND-OR list.
With emphasis on the last sentence.
You write that dash does ultimately interpret this correctly but applies
it too late, after fd 0 is already /dev/null.
Perhaps because this is not merely a no-op (as 0<&0 is) and that dash does
attempt to do the right thing (too late), it suggests maybe dash should
change behaviour.
However you're right about portable shell scripts. I have to accept the
minimal interpretation and your solution is very reasonable, perhaps even
more explicit as well.
Many thanks for your explanation.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-02-10 10:24 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-10 1:11 Surprising behaviour when reading from a named pipe and standard input Earnestly
2026-02-10 2:00 ` Harald van Dijk
2026-02-10 10:24 ` Earnestly
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox