* 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