如何在PowerShell中使用FINDSTR查找搜索字符串中所有单词以任意顺序匹配的行
下面的如何在PowerShell中使用FINDSTR查找搜索字符串中所有单词以任意顺序匹配的行,powershell,boolean-logic,findstr,Powershell,Boolean Logic,Findstr,下面的findstr.exe命令几乎满足了我的要求,但还不够: findstr /s /i /c:"word1 word2 word3" *.abc 我使用了: /s用于搜索所有子文件夹 /c: 将指定的文本用作文字搜索字符串 /i指定搜索不区分大小写 *.abcabc类型的文件 上面的代码将word1 word2 word3作为文本查找,因此只查找按该确切顺序排列的单词 相比之下,我希望所有单词以任何顺序(以及逻辑、连接)单独匹配。 如果我从上面的命令中删除/c:,那么将返回与任何单词
findstr.exe
命令几乎满足了我的要求,但还不够:
findstr /s /i /c:"word1 word2 word3" *.abc
我使用了:
用于搜索所有子文件夹/s
将指定的文本用作文字搜索字符串/c:
指定搜索不区分大小写/i
abc类型的文件*.abc
word1 word2 word3
作为文本查找,因此只查找按该确切顺序排列的单词
相比之下,我希望所有单词以任何顺序(以及逻辑、连接)单独匹配。
如果我从上面的命令中删除/c:
,那么将返回与任何单词匹配的行(或逻辑,析取),这不是我想要的
这可以在PowerShell中完成吗?您可以使用
Select String
在多个文件中执行基于正则表达式的搜索
要将单个字符串中的所有多个搜索词与正则表达式匹配,必须使用:
在上面的示例中,第一个命令就是这样:
Get ChildItem
在当前目录中搜索文件-Filter*.abc
仅显示以*.abc
结尾的文件-递归
搜索所有子文件夹
然后,我们通过管道将生成的FileInfo对象传递到选择字符串,并使用以下正则表达式模式:
由于每个前瞻组只是为了正确性而被断言,并且字符串中的搜索位置从未改变,因此顺序并不重要
如果希望它匹配包含任何单词的字符串,可以使用简单的非捕获组:
Get-ChildItem -Filter *.abc -Recurse |Select-String -Pattern '\b(?:word1|word2|word3)\b'
当然,这些可以抽象成一个整体
我生成了param
块和Select Match
函数定义的大部分主体,如下所示:
$slsmeta = [System.Management.Automation.CommandMetadata]::new((Get-Command Select-String))
[System.Management.Automation.ProxyCommand]::Create($slsmeta)
然后删除不必要的参数(包括-AllMatches
和-Pattern
),然后添加模式生成器(参见内联注释):
注:
- 此答案的第一部分并不能解决OP的问题-有关解决方案,请参阅和;或者,请参阅此答案的底部,它提供了一个根据Mathias代码改编的通用解决方案
- (由于对问题的最初误读),答案的这一部分使用析取逻辑——至少有一个匹配搜索项的匹配行——这是唯一支持
findstr.exe
和PowerShell的选择字符串
(直接)的逻辑
- 相比之下,OP要求连接逻辑,这需要额外的工作
- 对于使用
Select String
将findstr.exe
命令转换为PowerShell,这部分答案可能仍然很有意义
问题中的findstr
命令的PowerShell等价物,但没有/c:
-
FINDSTR/s/i“word1 word2 word3”*.abc
- 是:
(Get ChildItem-File-Filter*.abc-Recurse|
选择String-SimpleMatch-Pattern'word1'、'word2'、'word3')。计数
/s
->Get ChildItem-File-Filter*.abc-Recurse
输出当前目录子树中匹配的所有文件*.abc
- 请注意,wile能够接受文件名模式(通配符表达式),例如
*.abc
,它不支持递归,因此需要单独的Get ChildItem
调用,其输出通过管道传输到Select String
findstr
->选择字符串
,PowerShell更灵活的对应项:
-SimpleMatch
指定将-Pattern
参数解释为文本,而不是正则表达式。请注意它们的默认值有何不同:
findstr
默认情况下需要文本(您可以使用/R
切换到正则表达式)
Select String
默认情况下需要正则表达式(您可以使用-SimpleMatch
切换到literal)
-i
->(默认行为);与大多数PowerShell一样,不区分大小写是选择字符串的默认行为
添加-区分大小写
,以更改该行为
“word1 word2 word3”
->-模式“word1”、“word2”、“word3”
指定模式数组会查找每行上至少一个模式的匹配项(析取逻辑)
- 也就是说,以下所有行都将匹配:
。。。word1…
,。。。word2…
,。。。word2 word1…
,。。。word3 word1 word2…
/c
->(…).Count
:选择字符串
输出表示匹配行的对象集合
,此表达式仅对匹配行进行计数。
输出的对象是实例,其中不仅包括匹配行,还包括关于输入的元数据和匹配内容的细节
解决方案,基于:
是仅析取选择字符串
cmdlet的仅合取包装函数,该cmdlet使用与后者完全相同的语法,但不支持-AllMatches
开关
也就是说,selectstringall
要求传递给它的所有模式-无论它们是正则表达式(默认情况下)还是文本(使用-SimpleMatch
)-匹配一行。^(?=.*\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
$slsmeta = [System.Management.Automation.CommandMetadata]::new((Get-Command Select-String))
[System.Management.Automation.ProxyCommand]::Create($slsmeta)
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
#>
}
Get-ChildItem -Filter *.abc -Recurse |Select-Match word1,word2,word3 -All
(Get-ChildItem -File -Filter *.abc -Recurse |
Select-StringAll -SimpleMatch word1, word2, word3).Count
findstr /s /i "word1" *.abc | findstr /i "word2" | findstr /i "word3"
Get-ChildItem -Filter '*.abc' -Recurse | Get-Content | Where-Object {
$_ -like '*word1*' -and
$_ -like '*word2*' -and
$_ -like '*word3*'
}
ls '*.abc' -r | cat | ? {
$_ -like '*word1*' -and
$_ -like '*word2*' -and
$_ -like '*word3*'
}
findstr /i /r /c:"word[1-3].*word[1-3].*word[1-3]" *.abc