¿Cómo dividir una cadena delimitada en una matriz en awk?


169

Cómo dividir la cadena cuando contiene símbolos de tubería |en ella. Quiero dividirlos para que estén en la matriz.

Lo intenté

echo "12:23:11" | awk '{split($0,a,":"); print a[3] a[2] a[1]}'

Que funciona bien. Si mi cadena es así "12|23|11", ¿cómo los divido en una matriz?


3
Tenga en cuenta que su salida está concatenando los elementos de la matriz, sin separador. Si, en cambio, desea que se separen OFS, pegue comas entre ellos, haciéndolos printver como argumentos separados.
dubiousjim

O puede usar sed:echo "12:23:11" | sed "s/.*://"
slushy

@slushy: tu comando no es para nada lo que el autor de la pregunta necesita. su comando ( echo "12:23:11" | sed "s/.*://") elimina todo hasta (e incluye) el último ":", manteniendo solo el "11" ... funciona para obtener el último número, pero necesitaría ser modificado (de una manera difícil de leer) para obtener el segundo número, etc. awk (y la división de awk) es mucho más elegante y legible.
Olivier Dulac

si necesita dividirse en un solo personaje, puede usarlocut
ccpizza

Respuestas:


274

Has probado:

echo "12|23|11" | awk '{split($0,a,"|"); print a[3],a[2],a[1]}'

2
@ Moohamed Saligh, si está en Solaris, debe usar / usr / xpg4 / bin / awk , dada la longitud de la cadena.
Dimitre Radoulov el

55
'no está funcionando para mí'. especialmente con dos puntos entre los valores repetidos y la configuración dividida para dividir en '|' ??? ¿Error de tipografía? Buena suerte a todos.
Shellter

1
Mejor con alguna explicación de sintaxis.
Alston

2
Esto no funcionará en GNU awk, porque el tercer argumento splites expresión regular, y |es un símbolo especial, que necesita ser escapado. Usosplit($0, a, "\|")
WhiteWind

1
@WhiteWind: otra forma de "garantizar" que |se ve como un carácter y no un símbolo especial es ponerlo entre []: es decir, split($0, a, "[|]") # Me gusta más que '\ |', en algunos casos, especialmente como alguna variante de regexp ( perl vs grep vs .. otros?) puede tener "|" interpretado literalmente y "\ |" visto como separador de expresiones regulares, en lugar de lo contrario ... ymmv
Olivier Dulac

119

Para dividir una cadena en una matriz awk, utilizamos la función split():

 awk '{split($0, a, ":")}'
 #           ^^  ^  ^^^
 #            |  |   |
 #       string  |   delimiter
 #               |
 #               array to store the pieces

Si no se proporciona un separador, utiliza el FS, que por defecto es el espacio:

$ awk '{split($0, a); print a[2]}' <<< "a:b c:d e"
c:d

Podemos dar un separador, por ejemplo ::

$ awk '{split($0, a, ":"); print a[2]}' <<< "a:b c:d e"
b c

Lo que es equivalente a configurarlo a través de FS:

$ awk -F: '{split($0, a); print a[1]}' <<< "a:b c:d e"
b c

En gawk también puede proporcionar el separador como una expresión regular:

$ awk '{split($0, a, ":*"); print a[2]}' <<< "a:::b c::d e" #note multiple :
b c

E incluso vea cuál era el delimitador en cada paso utilizando su cuarto parámetro:

$ awk '{split($0, a, ":*", sep); print a[2]; print sep[1]}' <<< "a:::b c::d e"
b c
:::

Citemos la página de manual de GNU awk :

split (cadena, matriz [, fieldsep [, seps]])

Dividir cadena en piezas separadas por fieldsep y almacene las piezas en la matriz y las cadenas de separación en la matriz de seps . La primera pieza se almacena en array[1], la segunda pieza en array[2], y así sucesivamente. El valor de cadena del tercer argumento, fieldsep , es una expresión regular que describe dónde dividir la cadena (del mismo modo que FS puede ser una expresión regular que describe dónde dividir los registros de entrada). Si se omite fieldsep , se utiliza el valor de FS . split()Devuelve el número de elementos creados. seps es una gawkextensión, seps[i]siendo la cadena de separación entrearray[i]y array[i+1]. Si fieldsep es un espacio único, entonces cualquier espacio en blanco inicial entraseps[0]y cualquier espacio en blanco final entra seps[n], donde n es el valor de retorno de split()(es decir, el número de elementos en la matriz).


solo mencione que está utilizando gnu awk, no awk regular (que no almacena separadores en seps [] y tiene otras limitaciones)
Olivier Dulac

17

¡Por favor sé más específico! ¿Qué quieres decir con "no funciona"? Publique la salida exacta (o mensaje de error), su sistema operativo y la versión awk:

% awk -F\| '{
  for (i = 0; ++i <= NF;)
    print i, $i
  }' <<<'12|23|11'
1 12
2 23
3 11

O, usando split:

% awk '{
  n = split($0, t, "|")
  for (i = 0; ++i <= n;)
    print i, t[i]
  }' <<<'12|23|11'
1 12
2 23
3 11

Editar: en Solaris necesitará usar POSIX awk ( / usr / xpg4 / bin / awk ) para procesar 4000 campos correctamente.


for(i = 0o for(i = 1?
PiotrNycz

i = 0, porque uso ++ i after (no i ++).
Dimitre Radoulov

3
Ok, no me di cuenta de esto. Creo firmemente que sería más legible for (i = 1; i <= n; ++i)...
PiotrNycz

5

No me gusta la echo "..." | awk ...solución, ya que llama innecesarias forky execllamadas del sistema.

Prefiero una solución de Dimitre con un pequeño giro

awk -F\| '{print $3 $2 $1}' <<<'12|23|11'

O una versión un poco más corta:

awk -F\| '$0=$3 $2 $1' <<<'12|23|11'

En este caso, se crea el registro de salida, que es una condición verdadera, por lo que se imprime.

En este caso específico el stdin redirección se puede ahorrar configurando un variable interna:

awk -v T='12|23|11' 'BEGIN{split(T,a,"|");print a[3] a[2] a[1]}'

solía bastante tiempo, pero en Esto podría ser manejado por la manipulación interna de la cadena. En el primer caso, la cadena original se divide por un terminador interno. En el segundo caso, se supone que la cadena siempre contiene pares de dígitos separados por un separador de un carácter.

T='12|23|11';echo -n ${T##*|};T=${T%|*};echo ${T#*|}${T%|*}
T='12|23|11';echo ${T:6}${T:3:2}${T:0:2}

El resultado en todos los casos es

112312

Creo que se suponía que el resultado final eran las referencias de variables de la matriz awk, independientemente del ejemplo de salida de impresión dado. Pero te perdiste un caso bash realmente fácil para proporcionar tu resultado final. T = '12: 23: 11 '; echo $ {T //:}
Daniel Liston el

@DanielListon ¡Tienes razón! ¡Gracias! No sabía que el final / se puede dejar en esta bashexpresión ...
Verdaderamente

4

En realidad, awktiene una función llamada enlace 'Variable del separador de campo de entrada' . Así es como se usa. No es realmente una matriz, pero usa las variables internas $. Para dividir una cadena simple es más fácil.

echo "12|23|11" | awk 'BEGIN {FS="|";} { print $1, $2, $3 }'

3
echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

Deberia trabajar.



1

¿Broma? :)

Qué tal si echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

Esta es mi salida:

p2> echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'
112312

así que supongo que está funcionando después de todo ...


¿Es por la longitud de la cuerda? desde entonces, mi longitud de cadena es de 4000. cualquier idea
Mohamed Saligh

1

Sé que esta es una pregunta vieja, pero pensé que tal vez alguien como mi truco. Especialmente porque esta solución no se limita a un número específico de elementos.

# Convert to an array
_ITEMS=($(echo "12|23|11" | tr '|' '\n'))

# Output array items
for _ITEM in "${_ITEMS[@]}"; do
  echo "Item: ${_ITEM}"
done

La salida será:

Item: 12
Item: 23
Item: 11
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.