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

748
Tensore

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

|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" 

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

6
@JakeGould я бы рассмотрел это как особый случай, потому что OP специально ищет решение PowerShell. Мало того, что это другой движок регулярных выражений (.NET против PCRE, который использует Notepad ++), но и текст замены на самом деле также указывается по-другому. Bob 6 лет назад 0

1 ответ на вопрос

8
Bob

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

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

Я постараюсь провести вас через это:

  • В вашем тексте есть раздел, который вы хотите сопоставить, и раздел, который вы хотите заменить . Традиционно, регулярное выражение заменяет всю строку поиска, поэтому вы должны использовать группу захвата, чтобы указать некоторую часть строки поиска, которая будет клонирована для вывода замены. Другой способ - использовать lookaround, что я и сделал здесь. PowerShell (.NET) - один из немногих языков регулярных выражений, который поддерживает просмотр за разной длины, поэтому нам повезло.
  • (?<=)Раздел является просмотром назад. Это означает, что все между =и )будет соответствовать, но не заменить . Так ^((\|[^|]*))+что используется в качестве условия - замена произойдет только в том случае, если этот бит соответствует тексту перед предполагаемой заменой.
  • ^((\|[^|]*))*[^|]*Раздел можно охарактеризовать как «от начала строки ( ^), матч наборы пяти |секунд, а затем сопоставить текст до следующего |».
    • Начало строки ^важно - в противном случае оно может совпадать в любом месте строки, и нет гарантии, сколько |s пришло раньше.
    • Потому что |имеет особое значение в регулярном выражении, оно должно быть экранированы: \|. Его не нужно экранировать, когда он находится внутри класса персонажа ( []).
    • [^|]*означает «текст до следующего |» - более технически, «как можно больше символов, чем |это возможно» - более технически «повторять [^|]класс символов столько раз, сколько это возможно, если этот класс символов соответствует любому символу, кроме |».
    • * означает «ноль или более повторений предыдущего символа, как можно больше»
    • Так (\|[^|]*)означает совпадение, |за которым следует как можно больше символов до следующего |. Это будет соответствовать|text
    • означает повторить предыдущий токен ровно 5 раз. Это в точности эквивалентно копированию предыдущего токена 5 раз. Так что это будет соответствовать|text|text|text|text|text
    • ((\|[^|]*))+это одно или несколько повторений всей этой группы. Так он может соответствовать |text|text|text|text|text, |text|text|text|text|text|text|text|text|text|textи т.д. - в упаковке 5. Причина, почему мы используем +вместо *есть, мы не хотим, чтобы соответствовать пустую группу и заменить очень первым |.
    • И это делает весь взгляд сзади, а это означает, что он заменит a только |с точным кратным 5 |с от него, с начала строки.
  • Далее следует \|фактический текст для замены, которому предшествует сопоставленный вид сзади.
  • Если взять ваш пример |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-й . Но метод lookbehind достаточно аккуратно обрабатывает ситуацию «минус первая».


А теперь замена строки.

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

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

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

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

Работал как в перчатке! Вы также ответили на несколько других вопросов о PS, который у меня был, вы ученый и джентльмен! Спасибо! Tensore 6 лет назад 0