如何在PowerShell中使用FINDSTR查找以任何顺序匹配搜索字符串中所有单词的行 [英] How to use FINDSTR in PowerShell to find lines where all words in the search string match in any order

查看:200
本文介绍了如何在PowerShell中使用FINDSTR查找以任何顺序匹配搜索字符串中所有单词的行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下findstr.exe命令几乎可以满足我的要求,但不完全可以:

The following findstr.exe command almost does what I want, but not quite:

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

我用过:

  • /s用于搜索所有子文件夹.
  • /c:
  • /s for searching all subfolders.
  • /c:

使用指定的文本作为文字搜索字符串

Uses specified text as a literal search string

  • /i指定搜索不区分大小写.
  • *.abc abc类型的文件.
  • /i Specifies that the search is not to be case-sensitive.
  • *.abc Files of type abc.
  • 上面的代码将word1 word2 word3查找为 literal ,因此只能找到准确顺序 中的单词.

    The above looks for word1 word2 word3 as a literal, and therefore only finds the words in that exact order.

    通过对比,我希望所有个单词以任何顺序( )分别匹配 >.

    By contrast, I want all words to match individually, in any order (AND logic, conjunction).

    如果我从上面的命令中删除了/c:,则返回与 any 个单词匹配的行(或逻辑,析取),这不是我想要的.

    If I remove /c: from the command above, then lines matching any of the words are returned (OR logic, disjunction), which is not what I want.

    这可以在PowerShell中完成吗?

    Can this be done in PowerShell?

    推荐答案

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

    You can use Select-String to do a regex based search through multiple files.

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

    To match all of multiple search terms in a single string with regular expressions, you'll have to use a lookaround assertion:

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

    在上面的示例中,第一个命令就是这样:

    In the above example, this is what's happening with the first command:

    Get-ChildItem -Filter *.abc -Recurse
    

    Get-ChildItem在当前目录中搜索文件
    -Filter *.abc仅向我们显示以*.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并使用以下正则表达式模式:

    We then pipe the resulting FileInfo objects to Select-String and use the following regex pattern:

    ^(?=.*\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
    

    由于只是为了确保正确性而断言了每个前瞻组,并且字符串中的搜索位置从不改变,所以顺序无关紧要.

    Since each lookahead group is just being asserted for correctness and the search position within the string never changes, the order doesn't matter.

    如果您希望它匹配包含 any 个单词的字符串,则可以使用一个简单的非捕获组:

    If you want it to match strings that contain any of the words, you can use a simple non-capturing group:

    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
    


    这些当然可以在我使用以下代码生成了param块和Select-Match函数定义的大部分内容:

    I generated the param block and most of the body of the Select-Match function definition below with:

    $slsmeta = [System.Management.Automation.CommandMetadata]::new((Get-Command Select-String))
    [System.Management.Automation.ProxyCommand]::Create($slsmeta)
    

    然后删除了不必要的参数(包括-AllMatches-Pattern),然后添加了模式生成器(请参见内联注释):

    Then removed unnecessary parameters (including -AllMatches and -Pattern), then added the pattern generator (see inline comments):

    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:

    Now you can use it like this, and it'll behave almost like Select-String:

    Get-ChildItem -Filter *.abc -Recurse |Select-Match word1,word2,word3 -All
    

    这篇关于如何在PowerShell中使用FINDSTR查找以任何顺序匹配搜索字符串中所有单词的行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆