Заменить каждую 6-ю трубу в powershell

Заменить каждую 6-ю трубу в powershell

Я понимаю, что задаю похожий вопрос, который уже задавался и на который был дан ответ, но я не смог экстраполировать нужный мне ответ, поскольку регулярные выражения и движок регулярных выражений достаточно различны. У меня есть журналы управления активами оборудования, которые разделены конвейером, но не разделены главным образом между конечными точками. Журналы выглядят так:

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

Я бы хотел заменить каждую шестую |на возврат каретки, чтобы выглядело так:

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

Самый близкий из найденных мной вариантов выбирает каждую конечную точку, но я не совсем уверен, как это использовать с помощью PowerShell.

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

Я знаком с командой замены в PS и предполагаю, что конечный результат будет примерно таким:

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

Заранее спасибо!

решение1

Ладно, это на самом деле немного сложно. Возможно, regex — не лучший инструмент для этой работы, но он может это сделать.

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

Я попробую вам это объяснить:

  • В вашем тексте есть раздел, который вы хотитесоответствоватьи раздел, который вы хотитезаменять. Традиционно регулярное выражение заменяет всю строку поиска, поэтому вы можете использоватьгруппа захватачтобы указать некоторую часть строки поиска, которая будет клонирована в заменяющий вывод. Другой способ — использоватьосмотреться, что я и сделал здесь. PowerShell (.NET) — один из немногих языков регулярных выражений, который поддерживаетпросмотры назад переменной длины, так что нам повезло.
  • Раздел (?<=)является ретроспективным. Это означает, что все, что находится между =и ), являетсясовпалоно нетзаменены. Так ^((\|[^|]*){5})+используется каксостояние- замена произойдет только в том случае, если этот бит совпадает с текстом перед предполагаемой заменой.
  • Раздел ^((\|[^|]*){5})*[^|]*можно описать так: «от начала строки ( ^) сопоставьте наборы из пяти |символов, а затем сопоставьте текст до следующего |».
    • Начало строки ^важно, иначе оно может совпадать с любым местом строки, и нет никакой гарантии, сколько букв |s было до этого.
    • Поскольку |имеет особое значение в регулярных выражениях, его необходимо экранировать: \|. Его не нужно экранировать, если он находится в классе символов ( []).
    • [^|]*означает «текст до следующего |» — более технически, «как можно больше символов, кроме |» — более технически «повторить [^|]класс символов столько раз, сколько возможно, где этот класс символов соответствует любому символу, кроме |».
    • *означает «ноль или более повторений предыдущего символа, столько, сколько возможно»
    • Так (\|[^|]*)означает совпадение |, за которым следует как можно больше символов до следующего |. Это будет соответствовать|text
    • {5}означает повторить предыдущий токен ровно 5 раз. Это в точности эквивалентно копированию-вставке предыдущего токена 5 раз. Так что это будет соответствовать|text|text|text|text|text
    • ((\|[^|]*){5})+является одним или несколькими повторениями всей этой группы. Поэтому он может соответствовать |text|text|text|text|text, |text|text|text|text|text|text|text|text|text|textи т. д. - кратно 5. Причина, по которой мы используем +вместо , *заключается в том, что мы не хотим соответствовать пустой группе и заменять самый первый |.
    • И это завершает весь просмотр назад, то есть он заменит только a |на точно кратное 5 |s позади него, от начала строки.
  • После этого следует указать \|фактический текст для замены, которому предшествует сопоставленный ретроспективный просмотр.
  • Если взять ваш пример |STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3, то это будет соответствовать следующему:

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

Вы заметите здесь (если вы еще этого не сделали), что вы на самом деле пытаетесь заменить все5-й |минус первый, не каждый6-й. Но метод ретроспективного анализа справляется с ситуацией «минус первый» довольно четко.


А теперь сменная струна.

  • Поскольку это PowerShell, когда мы хотим \n, мы на самом деле хотим, `nпотому что escape-символ PowerShell — `. Обратите внимание, что это необходимо только в строке замены; в самом регулярном выражении вы все равно будете использовать \nэту буквальную последовательность для передачи в механизм регулярных выражений.
  • И поскольку у вас есть лидирующая строка |в каждой строке, нам нужно добавить новую |после новой строки. Это работает, потому что ваши исходные строки не заканчиваются на |, поэтому нечего заменять в конце строк, поэтому у нас не получается дополнительная новая строка или завершающая |.

Если вы предпочитаете более традиционный метод захвата группы:

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

Разобраться, как это работает, мы оставляем в качестве упражнения для читателя ;) Совет: $1обратную ссылку необходимо экранировать (с помощью `), поскольку в противном случае PowerShell интерпретирует ее как переменную оболочки.

Связанный контент