That's because the default behaviour of the C runtime library is to buffer writes to stdout until a full block of data is written (some kilobytes, usually), unless stdout is connected to a terminal.
You'll get output once the middle grep has printed a full block, but then you have to wait again for the next block to fill, and so on. It's an optimization for throughput, and works much better when the left-hand command just does some task and terminates, instead of waiting for something.
GNU grep has the --line-buffered
option to turn off that buffering, so this should work better:
tail -f my_file.txt | grep --line-buffered foo | grep bar
The last grep
prints to the terminal so it's line buffered by default and doesn't need an option.
See Turn off buffering in pipe for generic solutions to the buffering issue.
In this particular case of two greps, you could use e.g. a single AWK instead as Stéphane Chazelas mentioned in a comment:
tail -f my_file.txt | awk '/foo/ && /bar/'
(Incidentally, you could also do things like awk '/foo/ && !/bar/'
, catching lines with foo
but no bar
.)
Doing the same in grep would be harder, as grep -e foo -e bar
matches any lines that contain either foo
or bar
. You'd need something like
... | grep -E -e 'foo.*bar|bar.*foo'
instead.
cat my_file.txt | grep foo | grep bar
will show the correct output for the contents ofmy_file.txt
present when executingcat
, right? The issue is only for the new lines written tomy_file.txt
while the command is running. – GACy20 Dec 23 '22 at 08:35