2016-11-23 3 views
5

Ich versuche, ein Datum aus einer Textdatei auszugeben. Dies ist der Inhalt:Regex gibt die komplette Zeile zurück anstelle der Übereinstimmung

Storage Manager Command Line Administrative Interface - Version 7, Ausgabe 1, Level 1.4 (c) Copyright by Corporation und andere (n) 1990, 2015 Alle Rechte vorbehalten.

Session etabliert mit Server tServer: Windows Server Version 7, Ausgabe 1, Level 5.200 Server Datum/Zeit: 2016.11.22 15.30.00 Letzter Zugang: 2016.11.22 15.25.00

ANS8000I Serverbefehl.

Ich muss das Datum/die Uhrzeit nach Server Datum/Uhrzeit extrahieren. Ich habe diesen Regex geschrieben:

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

Dies funktioniert perfekt in regex101. Siehe Beispiel auf https://regex101.com/r/MB7yB4/1 In Powershell reagiert es jedoch anders.

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

gibt

Server Datum/Zeit: 2016.11.22 16.30.00 Letzter Zugriff: 11/22/2016 15:37:19

und

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

gibt nichts.

Ich bin mir nicht sicher, warum das Spiel nicht das Gleiche ist.
Danke für jede Hilfe!

+0

'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} ') {$ Übereinstimmungen [0]} ' –

+0

Dein 2.' - match' Beispielbefehl ist identisch mit dem ersten. Bitte bearbeiten Sie es, um die Variation anzuzeigen, die wirklich nichts hervorgebracht hat (oder entfernen Sie einfach den 2. Befehl). – mklement0

Antwort

1

Der Operator -match gibt einen booleschen Wert zurück, der anzeigt, ob eine Übereinstimmung gefunden wurde oder nicht. Außerdem wird die Variable mit den Übereinstimmungsdaten (den gesamten Übereinstimmungs- und Erfassungsgruppenwerten) festgelegt. Sie müssen nur das ganze Spiel zuzugreifen:

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

Siehe Using -match and the $matches variable in PowerShell.

Beachten Sie, dass es keine Notwendigkeit gibt / synmbol in Powershell regexps entkommen ist, da dieses Zeichen nicht speziell ist, und regex Trennzeichen (die äußere /.../ wie in JS, PHP regexp) werden nicht verwendet, wenn Sie einen regulären Ausdruck in Powershell zu definieren.

+0

Danke !. Ich habe das versucht, aber dann bekomme ich "Kann nicht in ein Null-Array indizieren." – mitch2k

+0

Der Fehler zeigt an, dass "$ matches" entweder kein Array oder ein leeres Array ist. Hinweis: Ich habe dies mit dem von Ihnen geposteten Text getestet und ich habe eine Übereinstimmung gefunden. Bitte teilen Sie den vollständigen Code, den Sie verwenden. Probieren Sie auch '\ s' anstatt eines literalen Platzes im Muster, aber ich denke nicht, dass es der Täter sein sollte. –

+0

In der Tat, das funktioniert !. Danke – mitch2k

1

Es ist, weil Sie mehrere Zeilen passen, es ist die Linie herausziehen, die übereinstimmt, die im Anschluss an das einzelne Spiel von der Linie zu ziehen verwenden:

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

Wenn Sie mit langen REs handeln es macht Sinn, benannte Erfassungsgruppen zu verwenden. Wenn ein RE in mehrere geteilt wird, bleibt der Name gleich. Wenn sich der RE über mehrere Zeilen erstrecken sollte, sollten Sie (?smi) verwenden und um crlf mit . abzugleichen, müssen Sie den Inhalt mit der Option -raw abfragen. Ich benutze \ d anstelle von [0-9] nur um 3chars zu speichern.

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

Ausgabe

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

In Fällen wie diesem ich immer noch lieber .NET Regex-Klasse verwenden, passt Methode direkt - es ist schneller, präziser und ausführlich. Wenn Sie sicher sind, dass der erste Termin ist das Ergebnis, das Sie suchen, könnten Sie verwenden:

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

Ich werde persönlich setzen „Server Datum/Zeit:“ in die Regex, dann entfernen sie aus dem Ergebnis (und parsen gelöscht Ergebnis zu DateTime-Objekt, wenn es notwendig ist).

([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. Ein kurzer Ratschlag vermeidet es, var als Variablennamen auch für Tests zu verwenden. Wirklich schlechte Angewohnheit.

0

Zur Ergänzung Wiktor Stribiżew's helpful answer, die viele nützliche Hinweise und eine effektive Lösung enthält, aber das Verhalten des -match Operator mit Array Eingabe nicht richtig erklären:

  • Das Verhalten der -match Betreiber ändert sich, wenn Die LHS ist ein Array von Strings: anstelle eines booleschen, die übereinstimmenden Array-Elemente zurückgegeben werden. Effektiv führt -match dann Arrayfilterung durch.
    • Sie haben wahrscheinlich lesen Ihre Dateiinhalt in $var mit nur Get-Content, die die Linien als String Array statt einer einzelnen String zurückgibt. In PSv3 + liest der Schalter -Raw die gesamte Datei als einzelne Zeichenfolge ein.
    • Ihre Regex stimmte (nur) das fünfte Element des Eingabearrays ab (die fünfte Zeile aus der Datei), so dass das Element - die gesamte Zeile - zurückgegeben wurde.
  • Wie in Wiktor Antwort, können Sie die Einträge der automatisch erstellt $Matches hashtable um zugreifen müssen, um Informationen zugreifen, was die jüngste Verwendung von -match erfasst: $Matches[0] enthält, was die Regex als Ganzes erfasst , $Matches[1] was die erste (unbenannte) Erfassungsgruppe erfasst ($Matches[2] für die zweite, ...) und $Matches['<name>'] für mit der Bezeichnung Erfassungsgruppen, wie in LotPing's helpful answer gezeigt. ($Matches.0 ist nur eine alternative Syntax für $Matches[0], zum Beispiel).
  • Es ist besser einfachen Anführungszeichen Strings zu verwenden ('...') reguläre Ausdrücke zu definieren, so dass Powershell eigene String-Interpolation, die Strings in doppelten Anführungszeichen angewendet wird ("...") nicht in die Quere kommen.

Wenn es darum geht Extraktion mit einem regulären Ausdruck Strings ermöglicht -replace oft unter Verwendung eine prägnante Lösung:

$var -join "`n" -replace '(?s).*?(\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}:\d{1,2}).*', '$1' 

Der zusätzliche -join "`n" Schritt benötigt wird, um die Anordnung von Linien wieder zusammenzusetzen in $var in eine einzige Zeichenfolge, die als Eingabe an -replace übergeben wird.
Die folgende Erklärung zeigt, wie Get-Content -Raw verwendet wird, um die gesamte Datei als einzelne Zeichenfolge zu lesen, um mit zu beginnen.

Erläuterung:

# 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' 
Verwandte Themen