powershell - 如何在 PowerShell 中使用 FINDSTR 查找搜索字符串中所有单词以任何顺序匹配的行

标签 powershell boolean-logic findstr

下面的 findstr.exe 命令几乎完成了我想要的,但不完全是:

findstr /s /i /c:"word1 word2 word3" *.abc

我用过:
  • /s 用于搜索所有子文件夹。
  • /c:

    Uses specified text as a literal search string

  • /i 指定搜索不区分大小写。
  • *.abc abc 类型的文件。

  • 上面将 word1 word2 word3 作为字面量查找,因此只能以确切的顺序查找单词。

    相比之下, 我希望所有单词以任何顺序(AND 逻辑,连词)单独匹配

    如果我从上面的命令中删除 /c:,则返回匹配任何单词的行(OR 逻辑、析取),这不是我想要的。

    这可以在 PowerShell 中完成吗?

    最佳答案

    您可以使用 Select-String 对多个文件进行基于正则表达式的搜索。

    要将单个字符串中的所有多个搜索词与正则表达式匹配,您必须使用 a lookaround assertion :

    Get-ChildItem -Filter *.abc -Recurse |Select-String -Pattern '^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$'
    

    在上面的例子中,这是第一个命令发生的事情:

    Get-ChildItem -Filter *.abc -Recurse
    

    Get-ChildItem searches for files in the current directory
    -Filter *.abc shows us only files ending in *.abc
    -Recurse searches all subfolders



    然后我们将生成的 FileInfo 对象通过管道传输到 Select-String 并使用以下正则表达式模式:

    ^(?=.*\bword1\b)(?=.*\bword2\b)(?=.*\bword3\b).*$
    ^              # start of string  
     (?=           # open positive lookahead assertion containing
        .*         # any number of any characters (like * in wildcard matching)
          \b       # word boundary
            word1  # the literal string "word1"
          \b       # word boundary
     )             # close positive lookahead assertion
     ...           # repeat for remaining words
     .*            # any number of any characters
    $              # end of string
    


    由于每个前瞻组只是为了正确性而断言,并且字符串中的搜索位置永远不会改变,因此顺序无关紧要。

    如果您希望它匹配包含任何单词的字符串,您可以使用一个简单的非捕获组:
    Get-ChildItem -Filter *.abc -Recurse |Select-String -Pattern '\b(?:word1|word2|word3)\b'
    

    \b(?:word1|word2|word3)\b
    \b          # start of string  
      (?:       # open non-capturing group
         word1  # the literal string "word1"
         |      # or
         word2  # the literal string "word2"
         |      # or
         word3  # the literal string "word3"
      )         # close positive lookahead assertion
    \b          # end of string
    


    这些当然可以在 a simple proxy function 中抽象出来。

    我生成了 param 块和下面的 Select-Match 函数定义的大部分主体:
    $slsmeta = [System.Management.Automation.CommandMetadata]::new((Get-Command Select-String))
    [System.Management.Automation.ProxyCommand]::Create($slsmeta)
    

    然后删除不必要的参数(包括 -AllMatches-Pattern ),然后添加模式生成器(请参阅内联注释):
    function Select-Match
    {
        [CmdletBinding(DefaultParameterSetName='Any', HelpUri='http://go.microsoft.com/fwlink/?LinkID=113388')]
        param(
            [Parameter(Mandatory=$true, Position=0)]
            [string[]]
            ${Substring},
    
            [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
            [Alias('PSPath')]
            [string[]]
            ${LiteralPath},
    
            [Parameter(ParameterSetName='Any')]
            [switch]
            ${Any},
    
            [Parameter(ParameterSetName='Any')]
            [switch]
            ${All},
    
            [switch]
            ${CaseSensitive},
    
            [switch]
            ${NotMatch},
    
            [ValidateNotNullOrEmpty()]
            [ValidateSet('unicode','utf7','utf8','utf32','ascii','bigendianunicode','default','oem')]
            [string]
            ${Encoding},
    
            [ValidateNotNullOrEmpty()]
            [ValidateCount(1, 2)]
            [ValidateRange(0, 2147483647)]
            [int[]]
            ${Context}
        )
    
        begin
        {
            try {
                $outBuffer = $null
                if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
                {
                    $PSBoundParameters['OutBuffer'] = 1
                }
    
                # Escape literal input strings
                $EscapedStrings = foreach($term in $PSBoundParameters['Substring']){
                    [regex]::Escape($term)
                }
    
                # Construct pattern based on whether -Any or -All was specified 
                if($PSCmdlet.ParameterSetName -eq 'Any'){
                    $Pattern = '\b(?:{0})\b' -f ($EscapedStrings -join '|')
                } else {
                    $Clauses = foreach($EscapedString in $EscapedStrings){
                        '(?=.*\b{0}\b)' -f $_
                    }
                    $Pattern = '^{0}.*$' -f ($Clauses -join '')
                }
    
                # Remove the Substring parameter argument from PSBoundParameters
                $PSBoundParameters.Remove('Substring') |Out-Null
    
                # Add the Pattern parameter argument
                $PSBoundParameters['Pattern'] = $Pattern
    
                $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Select-String', [System.Management.Automation.CommandTypes]::Cmdlet)
                $scriptCmd = {& $wrappedCmd @PSBoundParameters }
                $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
                $steppablePipeline.Begin($PSCmdlet)
            } catch {
                throw
            }
        }
    
        process
        {
            try {
                $steppablePipeline.Process($_)
            } catch {
                throw
            }
        }
    
        end
        {
            try {
                $steppablePipeline.End()
            } catch {
                throw
            }
        }
        <#
    
        .ForwardHelpTargetName Microsoft.PowerShell.Utility\Select-String
        .ForwardHelpCategory Cmdlet
    
        #>
    
    }
    

    现在你可以像这样使用它,它的行为几乎像 Select-String :
    Get-ChildItem -Filter *.abc -Recurse |Select-Match word1,word2,word3 -All
    

    关于powershell - 如何在 PowerShell 中使用 FINDSTR 查找搜索字符串中所有单词以任何顺序匹配的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43450914/

    相关文章:

    powershell - PowerShell 嵌套 ForEach 循环的问题

    function - powershell 有没有办法捕获所有命名参数

    .net - PowerShell/.NET 4 GUI - 内部 Windows 图标?

    2 个 boolean 值的 JavaScript switch-case

    sql 查询 - true => true, false => true 或 false

    regex - 如何使用 FINDSTR 获取带有简单或双引号的行

    SVN 预提交 Hook

    powershell - 如何在 powershell 中使用 SetConsoleMode 更改控制台输出模式?

    Python 逻辑 `and` 比较列表返回错误结果

    Windows 'findstr' 命令 : Exclude results containing particular string