Ersetzen Sie jedes 6. Rohr in der Powershell

826
Tensore

Ich stelle fest, dass ich eine ähnliche Frage stelle, die bereits gestellt und beantwortet wurde, aber ich konnte die benötigte Antwort nicht extrapolieren, da die Regex- und Regex-Engine anders ist. Ich habe Hardware-Asset-Management-Protokolle, die nur durch Pipes begrenzt sind, aber nicht zwischen Endpunkten. Die Protokolle sehen so aus:

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

Was ich gerne tun würde, ist, jeden sechsten |durch einen Wagenrücklauf zu ersetzen, der so aussieht:

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

Der nächstgelegene Punkt, den ich bekommen habe, wählt jeden Endpunkt aus, aber ich bin nicht ganz sicher, wie er ihn mit Powershell verwenden kann.

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

Ich bin mit dem Befehl zum Ersetzen in PS vertraut, und ich stelle mir vor, das Endergebnis hätte etwas zu diesem Effekt:

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

Danke im Voraus!

6
@JakeGould Ich würde dies als einen Sonderfall betrachten, da das OP gezielt nach einer PowerShell-Lösung sucht. Es ist nicht nur eine andere Regex-Engine (.NET im Vergleich zu PCRE, die Notepad ++ verwendet), sondern auch der Ersetzungstext wird anders angegeben. Bob vor 6 Jahren 0

1 Antwort auf die Frage

8
Bob

Ok, das ist wirklich ein bisschen schwierig. Regex ist zwar nicht das beste Werkzeug für den Job, aber er kann es.

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

Ich werde versuchen, Sie durchzugehen:

  • Ihr Text hat einen gewünschten Abschnitt entsprechen und ein Abschnitt, den Sie möchten, ersetzen . Normalerweise ersetzt Regex die gesamte Suchzeichenfolge. Daher würden Sie eine Erfassungsgruppe verwenden, um einen Teil der Suchzeichenfolge anzugeben, der auf die Ersetzungsausgabe geklont werden soll. Eine andere Möglichkeit besteht darin, einen Lookaround zu verwenden, was ich hier gemacht habe. PowerShell (.NET) ist eine der wenigen Regex-Sprachen, die Look-Variable mit variabler Länge unterstützt . Wir haben also Glück.
  • Der (?<=)Abschnitt ist ein Rückblick. Das bedeutet, dass alles, was zwischen den =und )wird abgestimmt, aber nicht ersetzt . So ^((\|[^|]*))+wird als verwendete Bedingung - der Ersatz wird nur geschehen, wenn dieser Bit den Text vor dem vorgesehenen Ersatz entspricht.
  • Der ^((\|[^|]*))*[^|]*Abschnitt kann wie folgt zusammengefasst werden: "Vom Anfang der Zeile ( ^), Übereinstimmungen von fünf |s und dann den Text bis zur nächsten |".
    • Der Start der Zeile ^ist wichtig - ansonsten kann er überall in der Zeile übereinstimmen und es gibt keine Garantie dafür, wie viele |s zuvor kamen.
    • Da |eine besondere Bedeutung in regex hat, muss es maskiert werden: \|. Es muss nicht innerhalb einer Zeichenklasse ( []) maskiert werden.
    • [^|]*bedeutet "Text bis zum nächsten |" - technisch besser "so viele andere Zeichen |wie möglich" - technisch "wiederholen" die [^|]Zeichenklasse so oft wie möglich, wobei diese Zeichenklasse mit einem anderen Zeichen als |"" übereinstimmt .
    • * bedeutet "null oder mehr Wiederholungen des vorherigen Zeichens, so viele wie möglich"
    • Also (\|[^|]*)bedeutet Übereinstimmung, |gefolgt von so vielen Zeichen wie möglich bis zum nächsten |. Das wird passen|text
    • bedeutet, das vorherige Token genau fünfmal zu wiederholen. Es ist genau gleichbedeutend mit dem fünfmaligen Einfügen des vorhergehenden Token. Das wird also passen|text|text|text|text|text
    • ((\|[^|]*))+ist eine oder mehrere Wiederholungen der gesamten Gruppe. So kann es passen |text|text|text|text|text, |text|text|text|text|text|text|text|text|text|textusw. - in Vielfachen von 5. Der Grund, warum wir +stattdessen verwenden, *ist, dass wir nicht die leere Gruppe zuordnen und die erste Gruppe ersetzen möchten |.
    • Und das macht den gesamten Look zurück, was bedeutet, dass a nur |ein Vielfaches von 5 Sekunden |hinter dem Start der Zeile ersetzt wird.
  • \|Anschließend wird ein Text als der eigentliche zu ersetzende Text eingefügt, dem der übereinstimmende Lookbehind vorangestellt wird.
  • Wenn Sie Ihr Beispiel verwenden |STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3, entspricht es dem Folgenden:

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

Sie werden hier feststellen (wenn Sie es noch nicht getan haben), dass Sie tatsächlich versuchen, jeden 5. | minus den ersten zu ersetzen, nicht jeden 6 .. Die lookbehind-Methode behandelt die Situation "minus dem ersten" ziemlich sauber.


Und jetzt die Ersatzschnur.

  • Da dies PowerShell ist, möchten \nwir, wenn wir wollen, tatsächlich, `nweil das Escape-Zeichen der PowerShell ist `. Beachten Sie, dass dies nur in der Ersetzungszeichenfolge erforderlich ist. In der Regex selbst würden Sie immer noch \ndiese literale Sequenz an die Regex-Engine übergeben.
  • Und da Sie |in jeder Zeile einen Vorsprung haben, müssen Sie |nach der neuen Zeile eine neue Zeile hinzufügen . Das klappt, weil Ihre ursprünglichen Zeilen nicht mit a enden |. Daher gibt es am Ende der Zeilen nichts zu ersetzen. Daher enden wir nicht mit einer zusätzlichen neuen Zeile oder einer nachfolgenden Zeile |.

Wenn Sie die traditionellere Aufnahmegruppenmethode bevorzugen:

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

Herauszufinden, wie dies funktioniert, bleibt dem Leser als Übung überlassen;) Tipp: Die $1Rückwärtsreferenz muss mit (Escapezeichen `) versehen werden, da sie von PowerShell andernfalls als Shell-Variable interpretiert wird.

Arbeitete wie ein Handschuh! Sie haben auch ein paar andere Fragen zu PS beantwortet, die ich hatte, Sie sind Gelehrter und Gentleman! Vielen Dank! Tensore vor 6 Jahren 0