Dividir una cadena con espacios en una nueva línea en PowerShell

Resuelto Caspian Peavyhouse asked hace 7 años • 6 respuestas

Estoy trabajando en un script de PowerShell donde tomo una entrada de una cadena larga (de un archivo CSV) en el formato:

Nombre del grupo uno
Nombre del grupo dos
Nombre del grupo tres
...

Estoy tratando de analizarlo con

($entry.'Group Name').split("`n ") | %{
    if ($_) {
        # Do something with the group name
        $_
    }
}

Quiero obtener resultados como:

Nombre del grupo uno
Nombre del grupo dos
Nombre del grupo tres
...

Pero sale como:

Nombre del grupo
uno Grupo dos ...



Caspian Peavyhouse avatar Jan 25 '17 01:01 Caspian Peavyhouse
Aceptado

Al aceptar la útil respuesta de Bacon Bits , indicó que resuelve su problema, pero aún queda la pregunta de qué pretendía que sucediera cuando pasó "`n "(es decir, una cadena PowerShell de 2 caracteres) al método [string]de la clase .Split().

Esta respuesta justifica el uso rutinario del propio -split operador de PowerShell en lugar del .Split() método , porque:

  • utiliza la sintaxis normal del operador de PowerShell
  • ofrece muchas más funciones
  • tiene menos sorpresas
  • proporciona estabilidad conductual a largo plazo

Existen diferencias clave entre -splity el .Split()método :

  • De forma predeterminada, -splitutiliza expresiones regulares para especificar el criterio de división; use la 'SimpleMatch'opción como tercer argumento RHS para usar cadenas literales en su lugar; por el contrario, el .Split()método sólo acepta cadenas literales.

  • También hay una forma unaria-split que divide por cualquier ejecución de espacios en blanco e ignora los espacios en blanco iniciales y finales, similar al awkcomportamiento predeterminado de ; esto es equivalente a llamar'...'.Split([string[]] $null, 'RemoveEmptyEntries')

  • -splitno distingue entre mayúsculas y minúsculas de forma predeterminada (como es típico en PowerShell); utilice el -csplitformulario para hacer coincidir mayúsculas y minúsculas; por el contrario, siempre.Split() distingue entre mayúsculas y minúsculas .

  • -splitacepta un LHS con valores de matriz y devuelve una concatenación de las matrices de tokens resultantes de dividir los elementos del LHS.

  • -split convierte implícitamente el LHS en cadena(s) ; por el contrario,.Split()sólo se puede invocar en algo que ya es un[string].

Nota: Ambos -split y .Split()le permiten limitar la cantidad de tokens devueltos con un segundo argumento opcional, que solo divide parte de la cadena de entrada, informando el resto de la cadena de entrada en el último elemento de la matriz devuelta.

Para conocer la historia completa, consulte Get-Help about_Split.

Sin embargo, el .Split()método tiene una ventaja : es más rápido que el -splitoperador; Entonces, si .Split() las características de son suficientes en un escenario determinado, puedes acelerar las cosas con él.

Ejemplos:

Nota: En los ejemplos siguientes que utilizan expresiones regulares, se utilizan cadenas entre comillas simples , con caracteres LF representados como una secuencia de escape de expresión regular \n en lugar de las `nsecuencias de escape que PowerShell admite en cualquier cadena entre comillas dobles , porque es preferible especificar expresiones regulares. como cadenas entre comillas simples , para evitar confusión entre lo que PowerShell expande desde el principio y lo que -splittermina viendo.

  • Dividido por cualquiera en un conjunto de caracteres , como expresión regular: "`n"(LF) y también " " (espacio simple):

    • "one two`n three four" -split '[\n ]'produce el equivalente de
      @( 'one', 'two', '', 'three', 'four' )
  • Dividido por una cadena , especificada como una expresión regular ::"`n "

    • "one two`n three four" -split '\n 'produce el equivalente de
      @( 'one two', 'three four' )
  • Dividido por una cadena literal :, "`n "usando la SimpleMatchopción:

    • "one two`n three four" -split "`n ", 0, 'SimpleMatch'produce lo mismo que el anterior; tenga en cuenta que 0es el argumento de número de tokens a devolver, que debe especificarse aquí por razones de sintaxis; 0indica que se deben devolver todos los tokens.
  • Utilice grupos de captura ( (...)) en la expresión regular del separador para incluir (partes de) separadores en la matriz de resultados:

    • 'a/b' -split '(/)'produce el equivalente de@('a', '/', 'b')
    • Alternativamente, use una aserción positiva de anticipación ( (?=...)) para hacer que los separadores formen parte de los elementos : 'a/b/c' -split '(?=/)'produce el equivalente de
      @( 'a', '/b', '/c' )
  • Limitar el número de tokens :

    • 'one two three four' -split ' ', 3produce el equivalente a
      @( 'one', 'two', 'three four' ), es decir, el tercer token recibió el resto de la cadena de entrada.

    • Advertencia : los elementos que son (partes de) separadores capturados a través de un grupo de captura en la expresión regular del separador no cuentan para el límite especificado; por ejemplo,
      'a/b/c' -split '(/)', 2produce @( 'a', '/', 'b/c' ), es decir, 3 elementos en total.

  • Dividido por cualquier conjunto de espacios en blanco (forma unaria) :

    • -split "`n one `n`n two `t `t three`n`n"produce el equivalente de
      @( 'one', 'two', 'three' )

String.Split()-errores del método:

Tener acceso al método de .NET Framework si es necesario es una opción maravillosa que le permite hacer en PowerShell la mayor parte de lo que pueden hacer los lenguajes .NET compilados.
Sin embargo, hay cosas que PowerShell tiene que hacer detrás de escena que normalmente son útiles, pero que también pueden ser riesgosas :

Por ejemplo, 'foo'.Split("`n ")hace que PowerShell convierta implícitamente la cadena "`n " en una matriz de caracteres ( [char[]]) antes de llamar .Split()(la coincidencia más cercana entre las sobrecargas de métodos), lo que puede ser inesperado.

Es posible que su intención haya sido dividir por cadena "`n " , pero la sobrecarga del método invocada terminó interpretando su cadena como un conjunto de caracteres individuales por los cuales dividir la entrada.

Por cierto, la edición PowerShell Core multiplataforma tiene una .Split()sobrecarga adicional que ahora acepta directamente un [string]argumento, por lo que la misma llamada se comporta de manera diferente allí.

Este comportamiento cambiante fuera del control de PowerShell es en sí mismo una buena razón para preferir soluciones exclusivas de PowerShell ; para obtener una explicación de por qué dichos cambios están fuera del control de PowerShell, consulte este número de GitHub .

Puede evitar estos errores escribiendo explícitamente, pero eso es engorroso y fácil de olvidar.
Caso en punto:

En Windows PowerShell, si realmente desea dividir por cadena "`n " , esto es lo que debe hacer:

PS> "one`n two".Split([string[]] "`n ", 'None')
one
two

Tenga en cuenta la conversión necesaria [string[]], aunque solo se pase una cadena, y el uso requerido del parámetro de opción ( None).

Por el contrario, si desea dividir por un conjunto de caracteres en PowerShell Core :

PS> "one`ntwo three".Split([char[]] "`n ")
one
two
three

Sin el [char[]]reparto, "`n "se consideraría una sola cuerda por la que dividirse.

mklement0 avatar Jan 28 '2017 00:01 mklement0

El argumento de cadena String.Split()es una lista de caracteres para dividir, no una secuencia de caracteres para hacer coincidir y luego dividir. Su código existente se dividirá en una nueva línea y se dividirá en un espacio.

Si solo desea dividir en nueva línea, use:

.split("`n")

Si desea dividir la secuencia de caracteres de una nueva línea seguida inmediatamente por un espacio, puede usar Regex.Split():

[Regex]::Split($entry.'Group Name',"`n ") | ...

Alternativamente, puedes usar el -splitoperador, que también divide por una cadena y no por una lista de caracteres:

$entry.'Group Name' -split "`n "
Bacon Bits avatar Jan 24 '2017 21:01 Bacon Bits

Si estoy leyendo correctamente, tu llamada a .Splitpasa tanto en `ncomo en el carácter de espacio. Entonces, en realidad le estás pidiendo a PowerShell que convierta una cadena como "Group One Name"en una lista como @("Group", "One", "Name").

Si $entryes un registro único y está ejecutando esta línea una vez para cada uno de "Nombre del grupo uno", "Nombre del grupo dos" y "Nombre del grupo tres", entonces probablemente no necesite la llamada en .Splitabsoluto, simplemente use $entry.'Group Name'directamente.

Jonathan Gilbert avatar Jan 24 '2017 18:01 Jonathan Gilbert