忽略二进制文件的 PowerShell 搜索脚本

标签 powershell grep

我真的很习惯做grep -iIr在 Unix shell 上,但我还没有能够获得等效的 PowerShell。

基本上,由于“-I”选项,上述命令递归搜索目标文件夹并忽略二进制文件。此选项也等效于 --binary-files=without-match选项,表示“将二进制文件视为与搜索字符串不匹配”

到目前为止我一直在使用 Get-ChildItems -r | Select-String作为我的 PowerShell grep 替换,偶尔使用 Where-Object添加。但我还没有想出一种方法来忽略所有二进制文件,例如 grep -I命令可以。

如何使用 Powershell 过滤或忽略二进制文件?

所以对于给定的路径,我只想要 Select-String搜索文本文件。

编辑:在谷歌上再过几个小时产生了这个问题 How to identify the contents of a file is ASCII or Binary .问题是“ASCII”,但我相信作者的意思是“文本编码”,就像我自己一样。

编辑:似乎是isBinary()需要编写来解决这个问题。可能是一个 C# 命令行实用程序,以使其更有用。

编辑:好像是什么grep正在做的是检查 ASCII NUL Byte 或 UTF-8 Overlong。如果这些存在,它会考虑文件二进制。这是单个 memchr() 调用。

最佳答案

在 Windows 上,文件扩展名通常就足够了:

# all C# and related files (projects, source control metadata, etc)
dir -r -fil *.cs* | ss foo

# exclude the binary types most likely to pollute your development workspace
dir -r -exclude *exe, *dll, *pdb | ss foo

# stick the first three lines in your $profile (refining them over time)
$bins = new-list string
$bins.AddRange( [string[]]@("exe", "dll", "pdb", "png", "mdf", "docx") )
function IsBin([System.IO.FileInfo]$item) { !$bins.Contains($item.extension.ToLower()) }
dir -r | ? { !IsBin($_) } | ss foo

但当然,文件扩展名并不完美。没有人喜欢输入长列表,无论如何,很多文件都被错误命名。

我认为 Unix 在文件系统中没有任何特殊的二进制与文本指示符。 (嗯,VMS 做到了,但我怀疑这就是您的 grep 习惯的来源。)我查看了 Grep -I 的实现,显然它只是基于文件第一块的快速-n-dirty 启发式方法。原来这是我的策略 a bit of experience和。所以这里是我关于选择适合 Windows 文本文件的启发式函数的建议:
  • 检查至少 1KB 的文件。许多文件格式都以一个看起来像文本的标题开头,但很快就会破坏你的解析器。现代硬件的工作方式是,读取 50 字节的 I/O 开销与读取 4KB 的 I/O 开销大致相同。
  • 如果您只关心纯 ASCII,请在看到字符范围 [31-127 加 CR 和 LF] 之外的内容时立即退出。您可能不小心排除了一些聪明的 ASCII 艺术,但试图将这些情况与二进制垃圾分开是很重要的。
  • 如果你想处理 Unicode 文本,让 MS 库来处理这些脏活。这比你想象的要难。从 Powershell,您可以轻松访问 IMultiLang2 interface (COM) 或 Encoding.GetEncoding静态方法 (.NET)。当然,他们现在还只是猜测。 Raymond 对 Notepad detection algorithm 的评论(以及 Michael Kaplan 中的链接)在决定如何混合和匹配平台提供的库之前,值得一读。
  • 如果结果很重要——也就是说,一个缺陷会造成比让 grep 控制台困惑更糟糕的事情——那么不要害怕为了准确性而对某些文件扩展名进行硬编码。例如,尽管 *.PDF 文件是二进制格式,但偶尔会在前面有几 KB 的文本,从而导致上面链接的臭名昭著的错误。同样,如果您的文件扩展名可能包含 XML 或类似 XML 的数据,您可以尝试类似于 Visual Studio's HTML editor 的检测方案。 . (SourceSafe 2005 实际上在某些情况下借用了这个算法)
  • 无论发生什么,都有一个合理的备份计划。

  • 例如,这里是快速 ASCII 检测器:
    function IsAscii([System.IO.FileInfo]$item)
    {
        begin 
        { 
            $validList = new-list byte
            $validList.AddRange([byte[]] (10,13) )
            $validList.AddRange([byte[]] (31..127) )
        }
    
        process
        {
            try 
            {
                $reader = $item.Open([System.IO.FileMode]::Open)
                $bytes = new-object byte[] 1024
                $numRead = $reader.Read($bytes, 0, $bytes.Count)
    
                for($i=0; $i -lt $numRead; ++$i)
                {
                    if (!$validList.Contains($bytes[$i]))
                        { return $false }
                }
                $true
            }
            finally
            {
                if ($reader)
                    { $reader.Dispose() }
            }
        }
    }
    

    我所针对的使用模式是在“dir”和“ss”之间的管道中插入的 where-object 子句。还有其他方法,具体取决于您的脚本风格。

    沿建议的路径之一改进检测算法留给读者。

    编辑:我开始在我自己的评论中回复您的评论,但时间太长了......

    上面,我从白名单已知良好序列的 POV 中查看了问题。在我维护的应用程序中,错误地将二进制文件存储为文本会产生比相反情况更糟糕的后果。对于选择要使用的 FTP 传输模式或要发送到电子邮件服务器的 MIME 编码类型等的场景,情况也是如此。

    在其他情况下,将明显伪造的内容列入黑名单并允许将其他所有内容称为文本是一种同样有效的技术。虽然 U+0000 是一个有效的代码点,但在现实世界的文本中几乎找不到。同时,\00 在结构化二进制文件中很常见(即,每当固定字节长度的字段需要填充时),因此它是一个非常简单的黑名单。 VSS 6.0 单独使用此检查并没有问题。

    旁白: *.zip 文件是检查\0 风险更大的情况。与大多数二进制文件不同,它们的结构化“页眉”(页脚?)块位于末尾,而不是开头。假设理想的熵压缩,前 1KB 中没有\0 的几率是 (1-1/256)^1024 或大约 2%。幸运的是,只需扫描 4KB 集群 NTFS 读取的其余部分即可将风险降低至 0.00001%,而无需更改算法或编写其他特殊情况。

    要排除无效的 UTF-8,请将\C0-C1 和\F8-FD 和\FE-FF(一旦您寻找到可能的 BOM 之后)添加到黑名单。非常不完整,因为您实际上并没有验证序列,但对于您的目的来说已经足够接近了。如果您想获得比这更有趣的东西,是时候调用 IMultiLang2::DetectInputCodepage 等平台库之一了。

    不知道为什么\C8(十进制 200)在 Grep 的列表中。这不是过长的编码。例如,序列\C8\80代表(U+0200)。也许某些特定于 Unix 的东西。

    关于忽略二进制文件的 PowerShell 搜索脚本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1077634/

    相关文章:

    node.js - 如何使用 Gulp 在不同文件夹中调用 Powershell 脚本

    visual-studio-2010 - Powershell 运行时

    json - 转换JSON文件时出现“Invalid JSON primitive”错误

    linux - 在名称中带有空格的文件夹中进行 Grep

    Bash 循环一个文件并根据在行中查找字符串将行放入另一个文件中

    PowerShell - 过滤唯一值

    awk 有两个文件,其中一个可能为空

    linux - 如何使用正则表达式进行 grep 以消除此堆栈跟踪?

    python - 如何从 Python/Perl 在 Exchange 2010 中创建 Exchange 邮箱