如何在PowerShell中使用FINDSTR查找以任何顺序匹配搜索字符串中所有单词的行 [英] How to use FINDSTR in PowerShell to find lines where all words in the search string match in any order
问题描述
以下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屋!