【问题标题】:Select-String: match a string only if it isn't preceded by a specific characterSelect-String:仅当字符串前面没有特定字符时才匹配该字符串
【发布时间】:2019-02-28 18:24:43
【问题描述】:

我有一个包含两个字符串之一的文件列表:

stuff”或“;stuff

我正在尝试编写一个仅返回包含“stuff”的文件的 PowerShell 脚本。下面的脚本当前返回所有文件,因为显然“stuff”是“;stuff”的子字符串

对于我的生活,我无法弄清楚如何只匹配包含“stuff”的文件,而没有前面的;

Get-Content "C:\temp\list\list.txt" |
  Where-Object { Select-String -Quiet -Pattern "stuff" -SimpleMatch $_ }

注意:C:\temp\list\list.txt 包含一个文件路径列表,每个路径都传递给Select-String

感谢您的帮助。

【问题讨论】:

  • 正则表达式怎么样? Select-String -Pattern '[^;]stuff'
  • 如果您的目标是选择字面上为stuff 而不是;stuff 的模式,您可以使用否定集(不要使用-SimpleMatch):-Pattern '[^;]stuff'
  • 使用 [^;]stuff 产生零结果,我不确定为什么这样做有意义。在玩了^stuff 之后确实产生了我正在寻找的结果,但我仍然不知道为什么一个工作与另一个工作。
  • 另一种选择是[^;]?,但消极的后视是工作的正确工具。
  • @TheIncorrigible1:[^;]?stuff 也很诱人,但也不起作用:';stuff' -match '[^;]?stuff' -> $true

标签: regex powershell regex-negation regex-lookarounds select-string


【解决方案1】:

您无法使用 literal 子字符串搜索 (-SimpleMatch) 执行所需的匹配。

相反,使用 regex否定后向断言 ((?<!..)) 以排除前面有 stuff 的子字符串; 字符:(?<!;)stuff

应用于您的命令:

Get-Content "C:\temp\list\list.txt" | 
  Where-Object { Select-String -Quiet -Pattern '(?<!;)stuff' -LiteralPath $_ }

正则表达式陷阱:

  • 使用 否定 (^) 字符集 ([...]) 代替 [^;]stuff 很诱人(请参阅 this answer);但是,如果stuff 出现在一行的开头,这不会按预期工作,因为字符集 - 无论是否否定 - 只匹配实际的 字符,而不是行首位置

  • 然后很容易将? 应用于否定字符集(对于 可选 匹配 - 0 或 1 次出现):[^;]?stuff。但是,这将再次匹配包含;stuff 的字符串,因为stuff 在技术上 前面是否定字符集的“0 次重复出现”;因此,';stuff' -match '[^;]?stuff' 产生 $true

只有后视断言在这种情况下正常工作 - 请参阅regular-expressions.info

【讨论】:

    【解决方案2】:

    为了补充 @mklement0 的回答,我建议使用另一种方法来使您的代码更易于阅读和理解:

    #requires -Version 4
    @(Get-Content -Path 'C:\Temp\list\list.txt').
        ForEach([IO.FileInfo]).
        Where({ $PSItem | Select-String -Pattern '(?<!;)stuff' -Quiet })
    

    这会将您的字符串转换为对象 (System.IO.FilePath) 并使用数组函数 ForEachWhere 来简洁/简洁。此外,这允许您将-Path 参数接受的路径作为对象 传递到Select-String 以使其更易于理解(我发现长长的参数集列表难以阅读)。

    【讨论】:

    • 谢谢。我最初也在玩文件信息输入,但最后我认为只需将$_ 更改为-LiteralPath $_ 会使 OP 的代码更清晰。您可能为了概念清晰而选择了@(...),但让我指出.ForEach().Where() 甚至可以在标量上工作。
    • @mklement0 是吗?我有这个假设的问题,即由于非数组而引发异常,但这可能是一些极端情况或不正确的使用
    • AFAIK,是的:'foo'.foreach({ "[$_]" })(1).foreach({ "[$_]" })。你能举一个它不起作用的例子吗?
    • @mklement0 没想到。它咬了我一次,所以我开始在需要或想要的任何地方使用@( ) ForEach/Where
    • 我明白了; @(...) 当然没有坏处,但最好知道是否可以依赖标量的使用;如果确实发现异常,请在 GitHub 上提出问题。
    【解决方案3】:

    发布的示例代码实际上不会运行,因为它会将每一行视为-Path 值。

    你需要的是获取内容,选择你想要的字符串,然后用Where-Object过滤结果

    Get-Content "C:\temp\list\list.txt" | Select-String -Pattern "stuff" | Where-Object {$_ -notmatch ";stuff"}
    

    如果需要,您可以创建更复杂的正则表达式,但这取决于文件中的结果数据是什么样的

    【讨论】:

    • list.txt 是应搜索其内容的文件列表。您的命令会搜索列表本身。
    猜你喜欢
    • 2017-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-12
    • 2013-02-11
    • 1970-01-01
    • 1970-01-01
    • 2010-09-17
    相关资源
    最近更新 更多