I am really used to doing grep -iIr on the Unix shell but I haven’t been able to get a PowerShell equivalent yet.
Basically, the above command searches the target folders recursively and ignores binary files because of the “-I” option. This option is also equivalent to the --binary-files=without-match option, which says “treat binary files as not matching the search string”
So far I have been using Get-ChildItems -r | Select-String as my PowerShell grep replacement with the occasional Where-Object added. But I haven’t figured out a way to ignore all binary files like the grep -I command does.
How can binary files be filtered or ignored with Powershell?
So for a given path, I only want Select-String to search text files.
EDIT: A few more hours on Google produced this question How to identify the contents of a file is ASCII or Binary. The question says “ASCII” but I believe the writer meant “Text Encoded”, like myself.
EDIT: It seems that an isBinary() needs to be written to solve this issue. Probably a C# commandline utility to make it more useful.
EDIT: It seems that what grep is doing is checking for ASCII NUL Byte or UTF-8 Overlong. If those exists, it considers the file binary. This is a single memchr() call.
On Windows, file extensions are usually good enough:
But of course, file extensions are not perfect. Nobody likes typing long lists, and plenty of files are misnamed anyway.
I don’t think Unix has any special binary vs text indicators in the filesystem. (Well, VMS did, but I doubt that’s the source of your grep habits.) I looked at the implementation of Grep -I, and apparently it’s just a quick-n-dirty heuristic based on the first chunk of the file. Turns out that’s a strategy I have a bit of experience with. So here’s my advice on choosing a heuristic function that is appropriate for Windows text files:
As an example, here’s the quick ASCII detector:
The usage pattern I’m targeting is a where-object clause inserted in the pipeline between “dir” and “ss”. There are other ways, depending on your scripting style.
Improving the detection algorithm along one of the suggested paths is left to the reader.
edit: I started replying to your comment in a comment of my own, but it got too long…
Above, I looked at the problem from the POV of whitelisting known-good sequences. In the application I maintained, incorrectly storing a binary as text had far worse consequences than vice versa. The same is true for scenarios where you are choosing which FTP transfer mode to use, or what kind of MIME encoding to send to an email server, etc.
In other scenarios, blacklisting the obviously bogus and allowing everything else to be called text is an equally valid technique. While U+0000 is a valid code point, it’s pretty much never found in real world text. Meanwhile, \00 is quite common in structured binary files (namely, whenever a fixed-byte-length field needs padding), so it makes a great simple blacklist. VSS 6.0 used this check alone and did ok.
Aside: *.zip files are a case where checking for \0 is riskier. Unlike most binaries, their structured “header” (footer?) block is at the end, not the beginning. Assuming ideal entropy compression, the chance of no \0 in the first 1KB is (1-1/256)^1024 or about 2%. Luckily, simply scanning the rest of the 4KB cluster NTFS read will drive the risk down to 0.00001% without having to change the algorithm or write another special case.
To exclude invalid UTF-8, add \C0-C1 and \F8-FD and \FE-FF (once you’ve seeked past the possible BOM) to the blacklist. Very incomplete since you’re not actually validating the sequences, but close enough for your purposes. If you want to get any fancier than this, it’s time to call one of the platform libraries like IMultiLang2::DetectInputCodepage.
Not sure why \C8 (200 decimal) is on Grep’s list. It’s not an overlong encoding. For example, the sequence \C8 \80 represents Ȁ (U+0200). Maybe something specific to Unix.