当我在PowerShell中使用Select-String(grep)时,如何仅返回匹配的正则表达式?

66
我试图在文件中查找一个模式。当我使用Select-String找到匹配项时,我不想要整行内容,只需要匹配的部分。
有没有参数可以实现这个功能?
例如:
如果我执行以下命令:
select-string .-.-.

文件中包含以下一行:

abc 1-2-3 abc

我想只得到1-2-3这个结果,而不是整行内容。

我想知道在Powershell中如何实现grep -o的功能。


如果您的正则表达式的一部分用于过滤要打印的行,但您不想打印该部分,则可以使用前瞻和后顾组(与grep -ohP相同)。假设您想获取keep 123 good中的数字,而不是drop 456 nogood中的数字,则可以使用(Select-String '(?>=keep )123(?= good)' myfile.txt).Matches.Value,结果为:123 - Andrew Spencer
8个回答

43

或者只需:

Select-String .-.-. .\test.txt -All | Select Matches

10
添加一些额外的信息:Select[-Object] Matches输出从Select-String[Microsoft.PowerShell.Commands.MatchInfo]类型输出对象复制的.Matches属性的自定义对象。如果您想要输出正则表达式捕获的实际文本,请改用ForEach-Object { $_.Matches[0].Value },假设每行只有一个匹配项。如果有多个匹配项 - 由于-All[Matches](等同于grep -o)可能会出现此情况 - 您可以在PSv3+中使用ForEach-Object { $_.Matches.Value },在PSv2中,您必须显式枚举$_.Matches集合。 - mklement0
12
echo "abcd" | Select-String -Pattern '(ab)' | Select Matches 返回 {0}。这句命令的意思是,在字符串 "abcd" 中查找匹配模式为 "(ab)" 的子字符串,并选择它们作为匹配项,最后返回第一个匹配项。 - john k
是的,这只会返回像 {0, 0, 0, 0, ...} 这样的东西,而不是实际匹配的值... - Douglas Gaskell
1
ForEach-Object { $_.Matches.Value } 每匹配一次就返回一个空行... - Douglas Gaskell

36

David走在正确的道路上了。[regex]是System.Text.RegularExpressions.Regex的类型加速器。

[regex]$regex = '.-.-.'
$regex.Matches('abc 1-2-3 abc') | foreach-object {$_.Value}
$regex.Matches('abc 1-2-3 abc 4-5-6') | foreach-object {$_.Value}

如果那样太啰嗦,你可以将其包装在一个函数中。


33

我尝试了其他方法:使用Select-String命令可以返回属性Matches,以此获取所有匹配项,需要指定参数-AllMatches,否则它只会返回第一个匹配项。

我的测试文件内容:

test test1 alk atest2 asdflkj alj test3 test
test test3 test4
test2

脚本:

select-string -Path c:\temp\select-string1.txt -Pattern 'test\d' -AllMatches | % { $_.Matches } | % { $_.Value }
返回
test1 #from line 1
test2 #from line 1
test3 #from line 1
test3 #from line 2
test4 #from line 2
test2 #from line 3

在technet.microsoft.com上使用Select-String


1
我无法让这个工作起来,我只能访问PS 1.0,而且看起来-AllMatches在至少1.0中无法识别。不管怎样,还是谢谢! - Skyler
哦,我使用的是v2,ctp3版本。很抱歉,我无法在v1上尝试解决这个问题。 - stej
1
在v2版本中,除了上下文之外,还增加了对匹配的支持。 - JasonMArcher

16

授人以鱼不如授人以渔的精神...

你需要将select-string命令的输出导入到Get-member中,这样可以查看对象具有哪些属性。一旦你这样做了,就会看到"Matches",然后通过将输出导入到| **Select-Object** Matches来选择它。

我的建议是使用类似于:select linenumber, filename, matches

例如:在stej的示例中:

sls .\test.txt -patt 'test\d' -All |select lineNumber,fileName,matches |ft -auto

LineNumber Filename Matches
---------- -------- -------
         1 test.txt {test1, test2, test3}
         2 test.txt {test3, test4}
         3 test.txt {test2}

13

以上的答案都对我没用。以下这个有效。

Get-Content -Path $pathToFile | Select-String -Pattern "'test\d'" | foreach {$_.Matches.Value}

Get-Content -Path $pathToFile | # Get-Content will divide into single lines for us

Select-String -Pattern "'test\d'" | # Define the Regex

foreach {$_.Matches.Value} # 只返回对象的Matches字段的值。 (这允许多个结果匹配。)


1
PowerShell 7.1需要在select-string中使用-AllMatches才能返回所有匹配项,例如"test1 test2 test3" | Select-String -Pattern "test\d" -AllMatches | foreach {$_.Matches.Value}。否则,select-string只会返回第一个匹配项。 - oleksa
在 PWSH 7.2.2 上没有使用“-All”也能正常工作,@oleksa。 - André Levy

11

使用更简单的 .prop 成员枚举 语法代替管道符 %select,这个魔法般地适用于多个元素:

(Select-String .-.-. .\test.txt -All).Matches.Value

更少的括号:

$m = Select-String .-.-. .\test.txt -All
$m.Matches.Value

这样做可以节省很多痛苦和打字! - Chris F Carroll

6
如果不想使用ForEach操作符,可以只使用管道和 Select -Expand。例如,要获取仅在 C:\ 后的路径,您可以使用以下内容:
Get-ChildItem | Select-String -Pattern "(C:\\)(.*)" | Select -Expand Matches | Select -Expand Groups | Where Name -eq 2 | Select -Expand Value

Where Name -eq 2 只选择指定正则表达式模式的第二个匹配项。


对于在使用前瞻/后顾时挑选匹配字符串,这是我使用的方法。cat myfile | select-string "(?<=before).+(?=after)" | Select -Expand Matches | Select -ExpandProperty Value - Chris F Carroll

2

这可不是 OP 要求的 PowerShell 解决方案,对吧? - aproximation

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接