Substitua cada 6º tubo no PowerShell

Substitua cada 6º tubo no PowerShell

Sei que estou fazendo uma pergunta semelhante que já foi feita e respondida, mas não consegui extrapolar a resposta que precisava, pois o mecanismo regex e o mecanismo regex são diferentes o suficiente. Eu tenho logs de gerenciamento de ativos de hardware que são delimitados por tubos, mas não são delimitados entre pontos de extremidade. Os registros ficam assim:

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

O que eu gostaria de fazer é substituir a cada 6 |por um retorno de carro para ficar assim:

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

O mais próximo que cheguei seleciona cada endpoint, mas não tenho certeza de como utilizá-lo usando o PowerShell.

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

Estou familiarizado com o comando replace no PS e imagino que o resultado final seria algo nesse sentido:

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

Desde já, obrigado!

Responder1

Ok, então este é realmente um pouco complicado. Indiscutivelmente, regex não é a melhor ferramenta para o trabalho, mas pode fazê-lo.

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

Vou tentar orientá-lo:

  • Seu texto tem uma seção que você desejacorrespondere uma seção que você desejasubstituir. Tradicionalmente, o regex substitui toda a string de pesquisa, então você usaria umgrupo de capturapara especificar alguma parte da string de pesquisa a ser clonada na saída de substituição. Outra maneira é usar umolhar em volta, que foi o que fiz aqui. PowerShell (.NET) é uma das poucas linguagens regex que oferece suportelookbehinds de comprimento variável, então estamos com sorte.
  • A (?<=)seção é uma retrospectiva. Isso significa que tudo entre o =e )écoincidemas nãosubstituído. Então ^((\|[^|]*){5})+é usado comodoença- a substituição só acontecerá se este bit corresponder ao texto anterior à substituição pretendida.
  • A ^((\|[^|]*){5})*[^|]*seção pode ser resumida como "desde o início da linha ( ^), combine conjuntos de cinco |s e depois combine o texto até o próximo |".
    • O início da linha ^é importante - caso contrário, pode corresponder a qualquer lugar da linha e não há garantia de quantos |s vieram antes.
    • Como |tem um significado especial em regex, precisa ser escapado: \|. Não precisa ser escapado quando estiver dentro de uma classe de caracteres ( []).
    • [^|]*significa "texto até o próximo |" - mais tecnicamente, "tantos caracteres diferentes |quanto possível" - mais tecnicamente "repetir a [^|]classe de caracteres tantas vezes quanto possível, onde essa classe de caracteres corresponde a qualquer caractere diferente de |".
    • *significa "zero ou mais repetições do caracter anterior, tantas quanto possível"
    • Então (\|[^|]*)significa correspondência |seguida por tantos caracteres quanto possível até o próximo |. Isto irá corresponder|text
    • {5}significa repetir o token anterior exatamente 5 vezes. É exatamente equivalente a copiar e colar o token anterior 5 vezes. Então isso vai combinar|text|text|text|text|text
    • ((\|[^|]*){5})+é uma ou mais repetições de todo o grupo. Portanto, pode corresponder a |text|text|text|text|text, |text|text|text|text|text|text|text|text|text|text, etc. - em múltiplos de 5. A razão pela qual usamos +em vez de *é que não queremos corresponder ao grupo vazio e substituir o primeiro |.
    • E isso faz com que todo o olhar fique para trás, o que significa que só substituirá a |por exatamente um múltiplo de 5 |s atrás dele, desde o início da linha.
  • Em seguida, coloque a \|como o texto real a ser substituído, precedido pelo lookbehind correspondente.
  • Tomando seu exemplo |STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3, corresponderá ao seguinte:

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

Você notará aqui (se ainda não o fez) que na verdade está tentando substituir todos5 ª |menos o primeiro, nem todos. Mas o método lookbehind lida com a situação "menos o primeiro" de maneira bastante limpa.


E agora a string de substituição.

  • Como este é o PowerShell, quando queremos \n, na verdade queremos `nporque o caractere de escape do PowerShell é `. Observe que isso só é necessário na string de substituição; no próprio regex você ainda usaria \npara passar essa sequência literal para o mecanismo de regex.
  • E como você tem um início |em cada linha, precisamos adicionar um novo |após a nova linha. Isso funciona porque suas linhas originais não terminam com a |, portanto não há nada para substituir no final das linhas, portanto não terminamos com uma nova linha extra nem no final |.

Se você preferir o método de grupo de captura mais tradicional:

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

Descobrir como isso funciona é deixado como um exercício para o leitor;) Dica: a $1referência anterior deve ser escapada (com `) porque, caso contrário, o PowerShell a interpretará como uma variável de shell.

informação relacionada