Reorganizar columnas usando awk


12

Estoy tratando de mover la séptima columna de mi archivo csv al final usando

awk -F '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}',OFS= "$file"

donde $ file es un archivo .csv en un directorio. Sin embargo, la salida es

awk:                          ^ syntax error

¿Alguien sabe cómo solucionar este error?


77
Al mostrar errores awk, debe mostrar todo. El ^indica la parte específica de la orden en el que se encuentra el error.
terdon

Respuestas:


10

La -Fopción necesita un argumento: -F,por ejemplo.

El final del awkscript debe separarse con un (espacio de caracteres) con el resto de los parámetros.

Si el separador de campo es ,y desea conservarlo, y si el número de columna es constante e inferior o igual a 11, intente esto:

awk -F, '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' OFS=, "$file"

8
@anuribs muy pocos programas lo permiten. La forma estándar es command file > newfile && mv newfile file. Dicho esto, nueva versión de GNU awkpara apoyar esto: gawk -i inplace '{blah blah}' file.
terdon

1
alternativamente, en lugar de mv newfile fileque pueda usar cat newfile > file ; rm -f newfile, esto preserva el inodo y los permisos de file.
cas

y generalmente es una buena idea usar mktempnombres de archivos temporales en lugar de codificar en scripts. por ejemplotf=$(mktemp) ; command file > "$tf" ; cat "$tf" > file ; rm -f "$tf"
cas

8

La solución más corta sería

awk -F',+' -v OFS=, '{$(NF+1)=$7; $7=""; $0=$0; $1=$1}1' file

No estoy seguro de si ,+funcionará en todas las awkversiones, pero funciona al menos en GNU awk, también con el -cmodo de compatibilidad.

Explicación:

  • $(NF+1)=$7: primero agregamos el séptimo campo al final de la línea (podría ser $12=$7en este caso)
  • $7="": en el siguiente paso se borra el séptimo campo (pero los delimitadores circundantes permanecen)
  • para eliminar delimitadores, necesitamos restablecer el registro completo (vía $0=$0) tratando múltiples comas como separador de campo (esto se hace vía -F',+', aquí +significa una o más veces), y también reorganizar el registro actual $1=$1para forzar la reconstrucción de la línea usando el campo de salida previamente establecido separador (establecido por una opción -v OFS=,)
  • Después de que todo el barajado esté hecho, estamos listos para imprimir el resultado con 1

Entrada de ejemplo:

1,2,3,4,5,6,7,8,9,10,11

salida

1,2,3,4,5,6,8,9,10,11,7

¿Qué pasa si otras columnas están en blanco? Pero sí, FS es una expresión regular en POSIX (si tiene varios caracteres), por lo que ,+debería funcionar.
Random832

(1) Entiendo que hacer que la séptima columna de datos de entrada "desaparezca", y no solo establecerla como nula, es una parte difícil de este problema. Pero, como dice Random832, su solución registra columnas en blanco (por ejemplo, all,ball,call,,,fallall,ball,call,fall). (2)  $(NF+1)=$7es un enfoque inteligente. En mi humilde opinión, $0 = $0 OFS $7es un poco más claro, solo un par de caracteres más largos, y parece hacer lo mismo. ¿Puedes pensar en una situación en la que $0 = $0 OFS $7no haga lo mismo que tu código?
G-Man dice 'Restablece a Monica' el

@ Random832 @ G-Man sí, algunos casos límite como campos en blanco, líneas en blanco o NF <7 deben tratarse por separado o uno debe reorganizar el código. Esto es solo una idea, no una "solución completa" para todos los casos generales, eso debería quedar claro. $0=$0 OFS $7es probablemente idéntico a $(NF+1)=$7, pero solo con el resto del código sin cambios, no en general.
jimmij

5

Si está imprimiendo con OFS=, sin un separador entre los campos, simplemente puede guardar el valor de $7en una variable, configurarlo $7para vaciarlo e imprimir la línea y la variable directamente. No necesita especificar todos los campos:

$ cat file
1,2,3,4,5,6,7,8
$ awk -F, -vOFS= '{k=$7; $7=""; print $0,k}' file 
12345687


3

Usted no ha dicho específicamente que quería utilizar awk, y qué decir que quería utilizar la edición in situ como el proporcionado por sed -i, por lo que aquí es una sed -ivariante. Por awklo general, es mejor trabajar con columnas, pero este es un caso en el que prefiero sed, porque naturalmente maneja números arbitrarios de columnas.

MOVECOL=7
N=$((MOVECOL-1))
sed -r -e "s/^(([^,]*,){$N})([^,]*),(.*)/\1\4,\3/" -i test.csv

Explicación:

  • -r selecciona expresiones regulares extendidas para evitar muchas barras invertidas
  • el primer grupo es $ N repeticiones de cadenas terminadas en coma, en otras palabras, las columnas antes de la que queremos mover, con una coma final
  • el segundo grupo es la repetición de $ N-th, nos olvidamos de eso
  • El tercer grupo es la columna que queremos mover, sin la coma final.
  • el cuarto grupo está compuesto por todas las columnas después de la que queremos mover, sin comas antes
  • reemplazamos con el primer grupo, el último grupo y la columna que extrajimos, insertando la coma según sea necesario.

Por supuesto, esto no funcionará con archivos que ocultan comas entre comillas (o peor, escapen de ellas), pero awk tampoco lo manejará sin algunas acrobacias serias. Si tiene ese problema, sería mejor con el perlmódulo Text:CSVo el pythonmódulo csv.


2

Un par de awkvariantes (suponiendo que su archivo esté dentro de la variable $file)

  • Aquí puede realizar un ciclo para todos los colores, imprimir con el separador de campo (OFS) e imprimir el terminador de registro (ORS) al final de la línea.

    awk  -F',' -v OFS=,                                \
    '{for(i=1;i<=NF;i++) if (i!=7) printf "%s",$i OFS; \
    printf "%s",$7;printf ORS}' "$file"
  • Aquí con el uso de una expresión regular y la gensub()función

    gawk -F',+' -v OFS=, '{$0=gensub(/\s*\S+/,"",7) OFS $7}1' "$file"

    matando a la 7 ª campo y la impresión de que al final de la línea.

    • $0 es todo el registro
    • $nes el enésimo registro
    • NF es el número de campos de la línea actual
    • OFS el separador archivado de salida
    • ORS el terminador de registro de salida
    • 1es el truco para decir a awk truee imprimir el valor predeterminado ( $0).

Actualización ...

Casi me olvido, es posible cambiar todas las columnas después de la 7ma .

awk  -F',' -v OFS=, '{tmp=$7; for(i=7;i<=NF;i++) $i=$(i+1); $NF=tmp}1 ' "$file"

(1) Podría decirse OFS $7que sería más robusto que "," $7. (2) Creo que eso ", " $7está mal, en la medida en que la pregunta indica que el OP no quiere espacios después de las comas. (Y, si los datos de entrada tenían espacios después de las comas, entonces $7sería ya comenzar con un espacio, y que estaría agregando un adicional.)
G-hombre dice 'Restablecer Mónica'

@ G-Man Fue principalmente para proponer algunas ideas, algunas variantes. Gracias, por el lugar, estoy de acuerdo OFS $7, no solo más robusto, sino aún más general ( "la prisa hace el desperdicio" )
Hastur
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.