Reemplace cada sexta tubería en PowerShell

Reemplace cada sexta tubería en PowerShell

Me doy cuenta de que estoy haciendo una pregunta similar que ya se hizo y respondió, pero no pude extrapolar la respuesta que necesitaba ya que la expresión regular y el motor de expresión regular son bastante diferentes. Tengo registros de administración de activos de hardware que están delimitados por tuberías pero no están delimitados entre puntos finales. Los registros se ven así:

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

Lo que me gustaría hacer es reemplazar cada sexto |con un retorno de carro para que se vea así:

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

Lo más cerca que he estado selecciona cada punto final, pero no estoy muy seguro de cómo utilizarlo usando PowerShell.

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

Estoy familiarizado con el comando de reemplazo en PS y me imagino que el resultado final sería algo parecido a este:

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

¡Gracias de antemano!

Respuesta1

Ok, entonces este es en realidad un poco complicado. Podría decirse que regex no es la mejor herramienta para el trabajo, pero puede hacerlo.

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

Intentaré guiarte a través de él:

  • Tu texto tiene una sección que deseasfósforoy una sección que quierasreemplazar. Tradicionalmente, la expresión regular reemplaza toda la cadena de búsqueda, por lo que usarías ungrupo de capturapara especificar alguna parte de la cadena de búsqueda que se clonará en la salida de reemplazo. Otra forma es utilizar unmira alrededor, que es lo que he hecho aquí. PowerShell (.NET) es uno de los pocos lenguajes de expresiones regulares que admitelookbacks de longitud variable, así que estamos de suerte.
  • La (?<=)sección es una mirada atrás. Eso significa todo entre el =y) esemparejadopero noreemplazado. Entonces^((\|[^|]*){5})+ se utiliza comocondición- el reemplazo solo se realizará si este bit coincide con el texto anterior al reemplazo previsto.
  • La ^((\|[^|]*){5})*[^|]*sección se puede resumir como "desde el inicio de la línea ( ^), haga coincidir conjuntos de cinco |segundos y luego haga coincidir el texto con el siguiente |".
    • El inicio de la línea ^es importante; de ​​lo contrario, puede coincidir en cualquier parte de la línea y no hay garantía de cuántos |s vinieron antes.
    • Debido a que |tiene un significado especial en expresiones regulares, es necesario escaparlo: \|. No es necesario utilizar un carácter de escape cuando se encuentra dentro de una clase de caracteres ( []).
    • [^|]*significa "texto hasta el siguiente |" - más técnicamente, "tantos caracteres distintos |como sea posible" - más técnicamente "repetir la [^|]clase de carácter tantas veces como sea posible, donde esa clase de carácter coincida con cualquier carácter que no sea |".
    • *significa "cero o más repeticiones del carácter anterior, tantas como sea posible"
    • Entonces (\|[^|]*)significa hacer coincidir |seguido de tantos personajes como sea posible hasta el siguiente |. Esto coincidirá|text
    • {5}significa repetir la ficha anterior exactamente 5 veces. Es exactamente equivalente a copiar y pegar el token anterior 5 veces. Entonces esto coincidirá|text|text|text|text|text
    • ((\|[^|]*){5})+es una o más repeticiones de todo ese grupo. Por lo tanto, puede coincidir con |text|text|text|text|text, |text|text|text|text|text|text|text|text|text|text, etc., en múltiplos de 5. La razón por la que usamos +en lugar de *es que no queremos hacer coincidir el grupo vacío y reemplazar el primero |.
    • Y eso hace que todo mire hacia atrás, lo que significa que solo reemplazará a |con exactamente un múltiplo de 5 |s detrás de él, desde el inicio de la línea.
  • Continúe con a \|como el texto real a reemplazar, precedido por la búsqueda hacia atrás coincidente.
  • Tomando su ejemplo |STATUS1|HOSTNAME1|IP1|MAC1|IS_WIRED1|STATUS2|HOSTNAME2|IP2|MAC2|IS_WIRED2|STATUS3|HOSTNAME3|IP3|MAC3|IS_WIRED3, coincidirá con lo siguiente:

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

Notarás aquí (si aún no lo has hecho) que en realidad estás intentando reemplazar cada5to |menos el primero, no todos6to. Pero el método de búsqueda atrás maneja la situación "menos el primero" de manera bastante limpia.


Y ahora la cuerda de repuesto.

  • Debido a que esto es PowerShell, cuando queremos \n, en realidad queremos `nporque el carácter de escape de PowerShell es `. Tenga en cuenta que esto sólo es necesario en la cadena de reemplazo; en la propia expresión regular aún usarías \npara pasar esa secuencia literal al motor de expresiones regulares.
  • Y como hay un interlineado |en cada línea, debemos agregar uno nuevo |después de la nueva línea. Esto funciona porque sus líneas originales no terminan con a |, por lo tanto, no hay nada que reemplazar al final de las líneas, por lo tanto, no terminamos con una nueva línea adicional ni al final |.

Si prefieres el método de captura grupal más tradicional:

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

Descubrir cómo funciona esto se deja como ejercicio para el lector;) Consejo: la $1referencia inversa debe escaparse (con `) porque, de lo contrario, PowerShell la interpreta como una variable de shell.

información relacionada