2016-11-23 70 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' 
相關問題