tee
(command) opens each file using fopen
, but sets _IONBF
(unbuffered) on each. It read
s from stdin, and fwrite
s to each FILE*.
pee
(command) popen
s each command, sets each to unbuffered, read
s from stdin, and fwrite
s to each FILE*.
popen
uses pipe(2), which has a capacity of 65536 bytes. Writes to a full buffer will block. pee
also uses /bin/sh to interpret the command, but I think that will not add any buffering/copying.
mkfifo
(command) uses mkfifo
(libc), which use pipes underneath, opening the file/pipe blocks until the other end is opened.
bash
<>() syntax (subst.c:5712) uses either pipe
or mkfifo
. pipe
if /dev/fds are supported. It does not use the c fopen
calls so does not set the buffering.
So all three variants (pee, tee >(), mkfifo ...) should end up with identical behaviour, reading from stdin and writing to pipes without buffering. The data is duplicated at each read (from kernel to user), and then again at each write (user back to kernel), I think tee
s fwrites will not cause an extra layer of copying (as there is no buffer). Memory usage could increase to a maximum of 65536 * num_readers + 1 * read_size (if no one is reading). tee
writes to stdout first, then each file/pipe in order.
Given this pee just works around other shells (fish!) lack of >() operator equivalent, there seems to be no need for it with bash. I prefer tee when you have bash, but pee is nice when you don't. The bash <() is not replaced by pee of course. Manually mkfifoing and redirecting is tricky and unlikely to deal with errors nicely.
pee
could probably be changed by implementing using the tee
library function (instead of fwrite). I think this would cause the input to be read at the speed of the fastest reader, and potentially fill up the kernel buffers.