You can use
tr to transform the stream into one you can grep normally:
stream | tr 'x\n' '\0x' | grep -qz xxx
This turns all
x bytes into null bytes, and all linefeed bytes into
xs, which can be grepped out as usual. That is, it moves one step along the path linefeed -> x -> null, so a sequence of three linefeeds will now be a sequence of three
xs, and no other
x bytes will occur (they will have become nulls terminating lines for the
This works with POSIX
grep -z is an extension. You may not need it - the separation behaviour isn't required here - and most
greps will handle binary data, but POSIX
grep is only required to work on text files so you're going to be depending on an extension one way or another.
If your real data is a text file, or just doesn't depend on binary-safe behaviour, you can probably survive on just
stream | tr 'x\n' '\nx' | grep -q xxx
- that is, just swapping the two bytes. This is nearly POSIX-compatible, but will likely work in practice just about anywhere (the issue is that the final line won't be terminated correctly, so it's not a text file, so
grep isn't strictly required to accept it).
One possible issue in either case is that a file with no existing
x bytes will be considered as one very long line, which may exceed the limits your
grep implementation will handle. Choosing another expected-to-be-common byte may work around that.
I was surprised that your original
grep -qz $'\n\n\n' command didn't work, but it had a false-positive problem for me - it seemed to behave like
grep -qz '' and always matched. I'm not sure why that is.