Given a function bar
that runs echo bar
every second forever, I expect bar | cat
to print one line every second. It does not. It doesn't print anything until I hit ^C
. There seems to be some superfluous buffering going on.
yes | cat
works just fine. bar | cat
works as expected in bash.
As to why this matters, please see this other question.
We have a command with a builtin in a pipeline:
echo bar | cat
bash forks new processes to execute builtins in pipelines. So there's two new processes: one for cat
, the other to run the builtin echo
. The processes are created with pipes connecting them, and they run independently.
fish always runs builtins directly, without creating new processes for them. So it executes echo bar
, then sends the output to cat
. The input and output for builtins and functions (but not external commands) is fully buffered. We're working on removing that limitation, but we're not there yet.
The bash behavior may seem more desirable in this case, but there's a heavy price for it, which is that builtins and functions do different things (or just don't work) inside pipelines. Example:
echo foo | read some_var ; echo $some_var
In fish, this will set some_var=foo
, as you would expect. In bash, this silently fails: the assignment "works," but only within the transient process spawned to execute the read
builtin, so the value isn't visible to the next command. This should give you some appreciation for why fish always runs builtins directly.