【问题标题】:One liner to find files that match two strings on different lines一个班轮查找与不同行上的两个字符串匹配的文件
【发布时间】:2021-11-10 00:09:11
【问题描述】:

目前在 4.0 版上。我很容易找到包含一个字符串的文件。尝试用不同行上的两个字符串做一些稍微复杂一点的事情并没有成功。问题显然是由于单独评估行的方式以及结果作为命令之间的对象传递的方式使得明显的解决方案不起作用。

# Works, so simple
Get-ChildItem | Select-String -Pattern "Test1" -List | Select Path

# Fail
Get-ChildItem | Select-String -Pattern "Test1.+Test2" -List | Select Path

# Fail
Get-ChildItem | Select-String -Pattern "Test1" -List | Select-String -Pattern "Test2" -List | Select Path

从概念上讲,这很简单,但我一直在寻找解决方案。在较新版本中可能更容易,但无法在所涉及的服务器上更新。我可以编写一个脚本来在 Python 中找到解决方案,但此时我想让 PowerShell 这样做是出于恶意。

了解每个命令返回的内容、如何获取底层对象数据、如何将该数据通过管道传输到后续命令等一直是我使用 PowerShell 时遇到的问题:(

【问题讨论】:

  • 我没有要测试的版本 4,但我认为应该是一样的。第一:Get-ChildItem 前面不需要Select-String。您可以直接提供路径。第二:参数-Pattern 采用模式数组。如果我做对了,它应该可以满足您的要求。 ;-)
  • 如果您的问题尚未得到完全解答,请考虑 accepting 提供答案或提供反馈。

标签: powershell powershell-4.0


【解决方案1】:

以下命令使用Where-Object 来测试我们的条件。


Get-ChildItem | Where-Object { 
    ($_ | Select-String -Pattern 'test1' -Quiet) -and
    ($_ | Select-String -Pattern 'test2' -Quiet) 
}

此行检查从 Get-ChildItem 管道传输的文件(由 $_ 表示)是否同时匹配“test1”和“test2”。使用-Quiet 参数将使Select-String 在找到匹配项时返回true,否则返回false。


Get-ChildItem -File | Where-Object {
    ($_ | Select-String -Pattern 'test1', 'test2' | 
        Group-Object -Property 'Pattern' ).Count -eq 2
    }

通过这一行,我给Select-String 2 个模式寻找。这不会确保两种模式都匹配,而是会为每个匹配的模式返回结果。 Select-String 返回的[Microsoft.PowerShell.Commands.MatchInfo] 对象上有一个名为Pattern 的属性。如果我使用Group-Object 对我通过属性“模式”返回的所有 MatchInfo 结果进行分组,那么我可以检查我是否最终得到了 2 个组对象,一个用于我的每个模式。如果为真,则对象由Where-Object返回


与 Python 或任何其他语言一样,您需要发现和学习一些命令/方法和参数,这将使您的生活更轻松,而且通常有不止一种方法可以做某事。

利用 PowerShell 提供的内置帮助来了解命令以及这些命令生成的对象。 Get-CommandGet-HelpGet-Member 可以说是 3 个最有用的 cmdlet,尤其是对于 PowerShell 新手或正在苦苦挣扎的人

【讨论】:

  • 做得很好,还有很好的发现技巧。但是,可以使用单个 Select-String 调用来解决问题(尽管取决于文件大小和匹配的文件内位置,即使是每个文件调用两次的解决方案也可能更快,因为短--Quiet 提供的电路)。我是否可以建议将您的命令分散到多行,这样就无需水平滚动即可到达有趣的部分?
  • 谢谢@mklement0。我确实也考虑过对文件的双重调用,并得出与您相同的结论,即通过使用“-Quiet”,一旦找到第一个匹配项,调用就会返回,这可能会减少完成所需的时间。我猜性能最差的情况是在文件底部附近的某处找到第一个术语的匹配项,然后找不到第二个术语的匹配项,导致文件几乎完全处理两次
【解决方案2】:

正如Olaf 指出的那样,Select-String-Pattern 参数接受一个数组 模式。

这样的命令会返回匹配任何个给定模式的行。

如果您想确定给定文件是否与给定模式的所有匹配(至少一次),则需要做更多工作:

$patterns = 'Test1', 'Test2'
Get-ChildItem -File |
  Select-String -Pattern $patterns | 
    Group-Object -Property Path | 
      Where-Object { 
        ($_.Group.Pattern | Select-Object -Unique).Count -eq $patterns.Count
      } | ForEach-Object Name

以上输出匹配所有模式的那些文件的完整路径。

  • Select-String 输出的Microsoft.PowerShell.Commands.MatchInfo 实例...

  • ...使用Group-Object cmdlet 按其.Path 属性(输入文件的完整路径)分组。

  • $_.Group.Pattern 根据Group-Object 输出的Microsoft.PowerShell.Commands.GroupInfo 实例,使用member enumeration 提取触发每个匹配的模式数组。

  • Select-Object -Unique 缩小与它包含的唯一(不同)模式实际匹配的模式数组。

  • 匹配输入模式计数的结果数组 (-eq $patterns.Count) 意味着找到 所有 输入模式(至少一次),并且在传递给 Where-Object 的脚本块中使用它意味着只输出匹配的组。

  • 最后,ForEach-Object Name 输出每个匹配组的.Name 属性,其中包含传递给Group-Object -Property 的分组属性的(字符串化)值,即手头案例中每个输入文件的完整路径。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-27
    • 1970-01-01
    • 1970-01-01
    • 2013-05-16
    • 1970-01-01
    • 1970-01-01
    • 2020-03-25
    • 2020-10-08
    相关资源
    最近更新 更多