GNU grep 2.24 RTFS
Conclusion: 2 and 2 cases only:
printf 'a\0' | grep 'a'
encoding error according to the C99
printf 'a\x80' | grep 'a'
\x80 cannot be the first byte of an UTF-8 Unicode point: UTF-8 - Description | en.wikipedia.org
Furthermore, as mentioned by Stéphane Chazelas What makes grep consider a file to be binary? | Unix & Linux Stack Exchange, those checks are only done up to the first buffer read of length TODO.
Only up to the first buffer read
So if a NUL or encoding error happens in the middle of a very large file, it might be grepped anyways.
I imagine this is for performance reasons.
E.g.: this prints the line:
printf '%10000000s\n\x80a' | grep 'a'
but this does not:
printf '%10s\n\x80a' | grep 'a'
The actual buffer size depends on how the file is read. E.g. compare:
(printf '\n\x80a') | grep 'a'
(printf '\n'; sleep 1; printf '\x80a') | grep 'a'
sleep, the first line gets passed to grep even if it is only 1 byte long because the process goes to sleep, and the second read does not check if the file is binary.
git clone git://git.savannah.gnu.org/grep.git
git checkout v2.24
Find where the stderr error message is encoded:
git grep 'Binary file'
Leads us to
if (!out_quiet && (encoding_error_output
|| (0 <= nlines_first_null && nlines_first_null < nlines)))
printf (_("Binary file %s matches\n"), filename);
If those variables were well named, we basically reached the conclusion.
Quick grepping for
encoding_error_output shows that the only code path that can modify it goes through
clen = mbrlen (p, buf + size - p, &mbs);
if ((size_t) -2 <= clen)
nlines_first_null and nlines
intmax_t nlines_first_null = -1;
nlines = 0;
so when a null is found
0 <= nlines_first_null becomes true.
TODO when can
nlines_first_null < nlines ever be false? I got lazy.
Does not define binary options grep - search a file for a pattern | pubs.opengroup.org , and GNU grep does not document it, so RTFS is the only way.