The reason why
tac file | grep foo | head -n 1
doesn't stop at the first match is because of buffering.
Normally, head -n 1
exits after reading a line. So grep
should get a SIGPIPE and exit as well as soon as it writes its second line.
But what happens is that because its output is not going to a terminal, grep
buffers it. That is, it's not writing it until it has accumulated enough (4096 bytes in my test with GNU grep).
What that means is that grep
will not exit before it has written 8192 bytes of data, so probably quite a few lines.
With GNU grep
, you can make it exit sooner by using --line-buffered
which tells it to write lines as soon as they are found regardless of whether goes to a terminal or not. So grep
would then exit upon the second line it finds.
But with GNU grep
anyway, you can use -m 1
instead as @terdon has shown, which is better as it exits at the first match.
If your grep
is not the GNU grep
, then you can use sed
or awk
instead. But tac
being a GNU command, I doubt you'll find a system with tac
where grep
is not GNU grep
.
tac file | sed "/$pattern/!d;q" # BRE
tac file | P=$pattern awk '$0 ~ ENVIRON["P"] {print; exit}' # ERE
Some systems have tail -r
to do the same thing as GNU tac
does.
Note that, for regular (seekable) files, tac
and tail -r
are efficient because they do read the files backward, they're not just reading the file fully in memory before printing it backward (as @slm's sed approach or tac
on non-regular files would).
On systems where neither tac
nor tail -r
are available, the only options are to implement the backward-reading by hand with programming languages like perl
or use:
grep -e "$pattern" file | tail -n1
Or:
sed "/$pattern/h;$!d;g" file
But those mean finding all the matches and only print the last one.