正则表达式返回完整的行而不是匹配

时间:2016-11-23 10:54:25

标签: regex powershell

我正在尝试从文本文件中取消日期。这是内容:

  

存储管理器   命令行管理界面 - 版本7,版本1,级别1.4   (c)公司和其他(1990),2015年的版权。保留所有权利。

     

与服务器TSERVER建立的会话:Windows     服务器版本7,版本1,级别5.200     服务器日期/时间:11/22/2016 15:30:00最后访问:11/22/2016 15:25:00

     

ANS8000I服务器命令。

我需要在服务器日期/时间之后提取日期/时间。我写了这个正则表达式:

/([0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2})/

这在regex101中完美运行。请参阅https://regex101.com/r/MB7yB4/1上的示例 然而,在Powershell中,它的反应不同。

$var -match "([0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2})"

给出

  

服务器日期/时间:11/22/2016 16:30:00最后访问:2016年11月22日   15点37分十九秒

$var -match "([0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2})"

什么都没有。

我不确定为什么比赛不一样。
感谢您的帮助!

5 个答案:

答案 0 :(得分:2)

-match运算符返回一个布尔值,显示是否找到匹配项。此外,它使用匹配数据(整个匹配和捕获组值)设置$matches变量。你只需要访问整场比赛:

if($var -match '[0-9]{1,2}/[0-9]{1,2}/[0-9]{4} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}') { $matches[0] }

请参阅Using -match and the $matches variable in PowerShell

请注意,在Powershell regexp中不需要转义/ synmbol,因为这个字符并不特殊,并且在定义时不使用正则表达式分隔符(JS,PHP regexp中的那些外部/.../) Powershell中的正则表达式。

答案 1 :(得分:1)

这是因为你匹配了几行,它正在拉出匹配的行,从行中取出单个匹配使用以下内容:

foreach ($line in $var) { if ($line -match "([0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2})") {write-output $matches[0]}}

答案 2 :(得分:1)

如果您正在处理冗长的RE,那么使用命名捕获组是有意义的。将RE拆分为多个时,名称保持不变。当RE可能跨越多行时,您应该使用(?smi)并且为了能够将crlf与.匹配,您必须使用-raw选项获取内容。我使用\ d而不是[0-9]来保存3个字符。

$var = Get-Content File.txt -Raw
if ($var -match "(?smi)Server date/time: (?<ServerDT>\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}:\d{1,2}).*access: (?<LastAc>\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}:\d{1,2})") { 
  "ServerDT  : "+$matches.ServerDT 
  "LastAccess: "+$matches.LastAc
}

输出

ServerDT  : 11/22/2016 15:30:00
LastAccess: 11/22/2016 15:25:00

答案 3 :(得分:1)

补充Wiktor Stribiżew's helpful answer,其中包含许多有用的指针和有效的解决方案,但没有正确解释 array 输入的-match运算符的行为:

  • 如果LHS是字符串的数组,则-match运算符的行为会发生变化:返回匹配数组元素而不是布尔值。实际上,-match然后执行数组过滤。
    • 您可能只用$var将文件内容读入Get-Content,这会将行返回为字符串 array 而不是单个字符串。在PSv3 +中,添加开关-Raw将整个文件作为单个字符串读取。
    • 你的正则表达式匹配(仅)输入数组的第5个元素(文件的第5行),因此返回了元素 - 整行。
  • 正如Wiktor的回答所述,您需要访问自动创建的$Matches哈希表的条目,以便访问有关最近使用-match 捕获的内容的信息$Matches[0]包含正则表达式整体捕获的内容,$Matches[1]第一个(未命名)捕获组捕获的内容(第二个$Matches[2],...)和{{1} } 命名捕获组,如LotPing's helpful answer中所示。 ($Matches['<name>']只是$Matches.0的替代语法,例如)。
  • 最好使用单引号字符串($Matches[0])来定义正则表达式,以便PowerShell自己的字符串插值应用于双引号字符串('...' )不会妨碍你。

使用正则表达式提取子字符串时,使用"..."通常可以提供更简洁的解决方案:

-replace

需要额外的$var -join "`n" -replace '(?s).*?(\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}:\d{1,2}).*', '$1' 步骤才能将-join "`n"中的行数组重新组合为单个字符串,以作为输入传递给$var
下面的说明显示了如何使用-replace将整个文件作为单个字符串读取。

<强>解释

Get-Content -Raw

答案 4 :(得分:0)

在这种情况下,我仍然喜欢直接使用.NET regex类匹配方法 - 它更快,更精确,更详细。如果您确定第一个日期是您搜索的结果,则可以使用:

[regex]::Matches($var,'\d{1,2}/\d{1,2}/\d{4}\s\d{1,2}:\d{1,2}:\d{1,2}')[0].value

我个人会将“服务器日期/时间:”放入正则表达式中,然后将其从结果中删除(如果有必要,则将清除结果解析为DateTime对象)。

([regex]::Matches($a,'Server\sdate/time:\s\d{1,2}/\d{1,2}/\d{4}\s\d{1,2}:\d{1,2}:\d{1,2}').value) -replace "Server date/time: ",''

PS。一个快速建议,即使对于测试,也要避免将var用作变量名。真是个坏习惯。