Ersetzen Sie jedes 6. Rohr in Powershell

Ersetzen Sie jedes 6. Rohr in Powershell

Mir ist klar, dass ich eine ähnliche Frage stelle, die bereits gestellt und beantwortet wurde, aber ich konnte die benötigte Antwort nicht extrapolieren, da der reguläre Ausdruck und die reguläre Ausdrucks-Engine unterschiedlich genug sind. Ich habe Hardware-Asset-Management-Protokolle, die durch Pipes getrennt sind, aber nicht durch Haupttrennzeichen zwischen den Endpunkten. Die Protokolle sehen folgendermaßen aus:

|STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3

Ich möchte jedes Sechstel |durch einen Wagenrücklauf ersetzen, sodass es folgendermaßen aussieht:

|STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1
|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2
|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3

Das Beste, was ich erreicht habe, ist die Auswahl jedes Endpunkts, aber ich bin nicht ganz sicher, wie ich dies mit Powershell nutzen kann.

[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*

Ich bin mit dem Ersetzungsbefehl in PS vertraut und stelle mir vor, dass das Endergebnis ungefähr so ​​aussehen würde:

$hosts = $hosts -replace "<highspeed_low_drag_velcro_snap_regex_here>","\r\n"

Dank im Voraus!

Antwort1

Ok, das hier ist tatsächlich ein bisschen knifflig. Regex ist wohl nicht das beste Tool für diese Aufgabe, aber es kann es.

-replace "(?<=^((\|[^|]*){5})+)\|","`n|"

Ich werde versuchen, es Ihnen näher zu bringen:

  • Ihr Text enthält einen Abschnitt, den Sieübereinstimmenund einen Abschnitt, den Sieersetzen. Traditionell ersetzt Regex den gesamten Suchbegriff. Sie würden also einenErfassungsgruppeum einen Teil des Suchbegriffs anzugeben, der in die Ersatzausgabe geklont werden soll. Eine andere Möglichkeit ist die Verwendung einesumschauen, was ich hier getan habe. PowerShell (.NET) ist eine der wenigen Regex-Sprachen, die unterstütztLookbehinds mit variabler Länge, also haben wir Glück.
  • Der (?<=)Abschnitt ist ein Lookbehind. Das heißt, alles zwischen =und )istabgestimmtaber nichtersetzt. ^((\|[^|]*){5})+Wird also verwendet alsZustand– der Ersatz erfolgt nur, wenn dieses Bit mit dem Text vor dem beabsichtigten Ersatz übereinstimmt.
  • Der ^((\|[^|]*){5})*[^|]*Abschnitt kann wie folgt zusammengefasst werden: „Vom Anfang der Zeile ( ^) aus Gruppen von fünf |s abgleichen, und dann den Text bis zum nächsten abgleichen |.“
    • Dabei ist der Zeilenanfang ^wichtig, da es sonst zu einer Übereinstimmung an beliebiger Stelle in der Zeile kommen kann und keine Garantie dafür besteht, wie viele |s davor kamen.
    • Da |es in regulären Ausdrücken eine besondere Bedeutung hat, muss es maskiert werden: \|. Innerhalb einer Zeichenklasse ( []) muss es nicht maskiert werden.
    • [^|]*bedeutet „Text bis zum nächsten |“ – technischer ausgedrückt: „So viele Zeichen außer „ |wie möglich“ – technischer ausgedrückt: „Wiederhole die [^|]Zeichenklasse so oft wie möglich, wobei diese Zeichenklasse mit jedem beliebigen Zeichen außer |„ übereinstimmt.“.
    • *bedeutet „null oder mehr Wiederholungen des vorherigen Zeichens, so viele wie möglich“
    • Das (\|[^|]*)bedeutet match |gefolgt von so vielen Zeichen wie möglich bis zum nächsten |. Dies wird matchen|text
    • {5}bedeutet, das vorherige Token genau 5 Mal zu wiederholen. Das entspricht genau dem Kopieren und Einfügen des vorhergehenden Tokens 5 Mal. Das passt also|text|text|text|text|text
    • ((\|[^|]*){5})+ist eine oder mehrere Wiederholungen dieser gesamten Gruppe. Es kann also mit |text|text|text|text|text, |text|text|text|text|text|text|text|text|text|text, usw. übereinstimmen – in Vielfachen von 5. Der Grund, warum wir +anstelle von verwenden *, ist, dass wir nicht mit der leeren Gruppe übereinstimmen und das allererste ersetzen möchten |.
    • |Und das macht den gesamten Lookbehind, was bedeutet, dass es nur ein s mit genau einem Vielfachen von 5 dahinter ersetzt |, vom Anfang der Zeile an.
  • Anschließend folgt ein \|als der eigentliche zu ersetzende Text, dem der übereinstimmende Lookbehind vorangestellt ist.
  • In Ihrem Beispiel |STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3wird es mit Folgendem übereinstimmen:

    |STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1**|**STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2**|**STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3
    

Sie werden hier bemerken (falls Sie es nicht bereits bemerkt haben), dass Sie tatsächlich versuchen, alle5. Platz |minus dem ersten, nicht jeder6. Platz. Aber die Lookbehind-Methode handhabt die „Minus-das-Erste“-Situation ziemlich sauber.


Und nun die Ersatzsaite.

  • Da es sich hier um PowerShell handelt, \nmöchten wir tatsächlich , wenn wir möchten `n, da das PowerShell-Escapezeichen ist `. Beachten Sie, dass dies nur in der Ersetzungszeichenfolge erforderlich ist. Im regulären Ausdruck selbst würden Sie weiterhin verwenden, \num diese Literalsequenz an die Regex-Engine zu übergeben.
  • Und weil jede Zeile einen führenden hat |, müssen wir nach der neuen Zeile einen neuen hinzufügen |. Das funktioniert, weil Ihre ursprünglichen Zeilen nicht mit einem enden |, daher gibt es am Ende der Zeilen nichts zu ersetzen, daher haben wir am Ende weder eine zusätzliche neue Zeile noch einen abschließenden |.

Wenn Sie die traditionellere Methode zur Erfassung von Gruppen bevorzugen:

-replace "((?:[^|]+\|){4}[^|]+)\|","`$1`n|"

Wie das funktioniert, bleibt dem Leser als Übung überlassen ;) Tipp: Die $1Rückreferenz muss maskiert werden (mit `), da PowerShell sie sonst als Shell-Variable interpretiert.

verwandte Informationen