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.
- 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 todos6º. 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`n
porque o caractere de escape do PowerShell é`
. Observe que isso só é necessário na string de substituição; no próprio regex você ainda usaria\n
para 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 $1
referência anterior deve ser escapada (com `
) porque, caso contrário, o PowerShell a interpretará como uma variável de shell.