2016-11-23 72 views
5

我想从文本文件中提取一个日期。这是内容:正则表达式返回完整的行而不是匹配

存储管理器 命令行管理界面 - 第7版,第1版,1.4级的公司和其他(一个或多个)1990年,2015年版权所有 (C)版权所有。与服务器建立TSERVER

会议:WINDOWS 服务器版本7,版本1,等级5.200 服务器日期/时间:2016年11月22日15:30:00最近访问:2016年11月22日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})" 

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

$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})" 

什么也没有。

我不知道为什么这场比赛是不一样的。
感谢您的帮助!

+0

'如果($变种-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]}' –

+0

Your 2nd'匹配样本命令与第一个命令相同。请编辑它以显示真正没有产生任何东西的变化(或者干脆删除第二个命令)。 – mklement0

回答

1

-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的正则表达式/ synmbol,由于该字符不是特殊的,并且正则表达式定界符(那些外/.../如JS,PHP的regexp)限定Powershell的正则表达式时不被使用。

+0

谢谢!我试过,但后来我得到“不能索引到一个空数组。” – mitch2k

+0

错误表明'$ matches'不是数组或空数组。注意我使用你发布的文本测试了这个,并且我得到了一个匹配。请分享您使用的完整代码。另外,在模式中尝试'\ s'而不是文字空间,但我认为它不应该是罪魁祸首。 –

+0

确实,这有效!谢谢 – mitch2k

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]}} 
1

如果你正在处理这确实让冗长的RE感觉使用命名的捕获组。当将RE分成几个时,名称保持不变。当RE可能跨越多行时,您应该使用(?smi)并且能够将crlf与.匹配,您必须使用-raw选项获取内容。我使用\ d而不是[0-9]来保存3chars。

$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 
0

在这样的情况下,我还是喜欢使用.NET正则表达式类直接匹配方法 - 它是速度更快,精确和详细。如果你相信,第一次约会是你搜索,你可以使用结果:

[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作为变量名,即使是测试。真的是坏习惯。

0

为了补充Wiktor Stribiżew's helpful answer,其包含了许多有用指针和有效的解决方案,但不与阵列输入正确解释-match操作者的行为:

  • -match操作者的变化,如果该行为LHS是字符串的数组:返回匹配的数组元素而不是布尔值。实际上,-match然后执行数组过滤。
    • 你可能只Get-Content阅读您的文件内容为$var,返回行作为一个字符串数组而不是一个字符串。在PSv3 +中,添加开关-Raw将整个文件读取为单个字符串。
    • 您的正则表达式匹配(仅)输入数组的第5个元素(文件的第5行),以便返回元素 - 整行 - 。
  • 作为Wiktor的的答案解释,您需要访问,以获取有关信息的自动生成$Matches哈希表的条目是什么最近一次使用的-match捕获$Matches[0]包含哪些正则表达式捕获作为一个整体,$Matches[1]第一个(未命名)捕获组捕获($Matches[2]第二个,...)和$Matches['<name>']对于命名为捕获组,如LotPing's helpful answer中所示。 (例如,$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将整个文件读取为单个字符串以开始。

说明:

# Read the text file as a *single* string, using -Raw. 
# Note: Without -Raw, you get an *array* of strings representing 
#  the individual lines. 
$var = Get-Content -Raw file.txt 

# Define the regex that matches the *entire* input, 
# with a single capture group capturing the substring of interest. 
# The regex: 
# - is prefixed with an inline-option expression, (?s), which ensures 
#  that . also matches a newline. 
# - starts with .*? a non-greedy expression matching any 
#  sequence of characters at the start of the input, 
# - followed by the original capture-group regex (though without escaping of/as \/, 
#  because that is not necessary in PowerShell, and \d used instead of [0-9]) 
# - ends with .*, a greedy expression that matches everything through the 
#  end of the input. 
$re = '(?s).*?(\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}:\d{1,2}).*' 

# Using -replace, we replace the entire input string - by virtue 
# of the overall regex matching the entire string - with only 
# what the capture group captured ($1). 
# The net effect is that only the capture group value is output. 
# With the sample input, this outputs '1/22/2016 15:30:00', the first 
# timestamp encountered. 
$var -replace $re, '$1' 
相关问题