Given the pipeline
printf '%s\n' 1 2 3 4 5 6 7 8 9 |
while read -r num
do
echo "$num" > /dev/stderr
echo "$num"
done |
while read -r num
do
echo $(( $num * 10 ))
[ "$num" -eq 5 ] && break
done
bash will output something like
1
2
10
3
20
4
30
5
40
50
6
7
8
9
to the terminal. The places in the single digit sequence where the two-digit numbers are inserted will be different each time you run the pipeline because of how buffering and/or scheduling work.
I would like to have the first while-read loop terminate at once when the second loop terminates; that is, I want the pipeline to not output any single digits after it has printed the number 50.
I tried using the command stdbuf -o0
to set the buffer to 0,
as suggested by this article
and Force line-buffering of stdout in a pipeline.
stdbuf -o0 printf '%s\n' 1 2 3 4 5 6 7 8 9 |
while read -r num
do
echo "$num" > /dev/stderr
echo "$num"
done |
while read -r num
do
echo $(( $num * 10 ))
[ "$num" -eq 5 ] && break
done
hoping to achieve this, but the printf command and the first loop still stay alive and can print their output after the last loop has printed the number 50.
I also tried set -o pipefail
and turning the second loop into the function:
g ()
{
while read -r num
do
echo $(( $num * 10 ))
[ "$num" -eq 5 ] && break
done
return 1
}
and modifying the pipeline accordingly:
printf '%s\n' 1 2 3 4 5 6 7 8 9 |
while read -r num
do
echo "$num" > /dev/stderr
echo "$num"
done |
g
(and then calling set +o pipefail
afterwards),
but that also fails to achieve what I want.
Apparently there is some fundamental error in my understanding of how pipelines and buffering work. Please explain to me what I am missing and how I can get the output I want.
break
. If you try with something likeprintf "%s\n" {1..50000}
so the first command will take longer to finish, you will see it is stopped long before we get to 50000. Does that help?setbuf
might affect), and the buffers in the pipe within the OS (which it doesn't). Also it misses thatstderr
customarily isn't buffered.50
.