6

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

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

То, что я хотел бы сделать, это заменить каждый 6-й | с возвратом каретки, чтобы выглядеть так:

|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 ответ1

8

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

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

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

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


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

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

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

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

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .