¿Cómo puedo hacer lo siguiente a un archivo CSV usando sed
o awk
?
- Eliminar una columna
- Duplicar una columna
- Mover una columna
Tengo una gran mesa con más de 200 filas, y no estoy tan familiarizado sed
.
¿Cómo puedo hacer lo siguiente a un archivo CSV usando sed
o awk
?
Tengo una gran mesa con más de 200 filas, y no estoy tan familiarizado sed
.
Respuestas:
Además de cómo cortar y reorganizar los campos (cubiertos en las otras respuestas), existe el problema de los extravagantes campos CSV.
Si sus datos entran en esta categoría "peculiar", un poco de filtrado previo y posterior puede encargarse de ello. Los filtros que se muestran a continuación requieren los personajes \x01
, \x02
, \x03
, \x04
que no aparecen en cualquier parte de sus datos.
Aquí están los filtros envueltos alrededor de un simple awk
volcado de campo.
Nota: el campo cinco tiene un diseño de "campo entre comillas" no válido / incompleto, pero es benigno al final de una fila (dependiendo del analizador CSV). Pero, por supuesto, causaría resultados problemáticos no acelerados si se cambiara de su posición actual de fin de fila .
Actualizar; user121196 ha señalado un error cuando una coma precede a una cita final. Aquí está la solución.
Los datos
cat <<'EOF' >file
field one,"fie,ld,two",field"three","field,\",four","field,five
"15111 N. Hayden Rd., Ste 160,",""
EOF
El código
sed -r 's/^/,/; s/\\"/\x01/g; s/,"([^"]*)"/,\x02\1\x03/g; s/,"/,\x02/; :MC; s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g; tMC; s/^,// ' file |
awk -F, '{ for(i=1; i<=NF; i++) printf "%s\n", $i; print NL}' |
sed -r 's/\x01/\\"/g; s/(\x02|\x03)/"/g; s/\x04/,/g'
La salida:
field one
"fie,ld,two"
field"three"
"field,\",four"
"field,five
"15111 N. Hayden Rd., Ste 160,"
""
Aquí está el pre filtro , expandido con comentarios.
El filtro posterior es solo una inversión de \x01
. \x02
` \x03
`\x04
sed -r '
s/^/,/ # add a leading comma delimiter
s/\\"/\x01/g # obfuscate escaped quotation-mark (\")
s/,"([^"]*)"/,\x02\1\x03/g # obfuscate quotation-marks
s/,"/,\x02/ # when no trailing quote on last field
:MC # obfuscate commas embedded in quotes
s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g
tMC
s/^,// # remove spurious leading delimiter
'
Esto depende de si su archivo CSV usa comas solo para delimitadores, o si tiene una locura como:
campo uno, "campo dos", campo tres
Esto supone que está utilizando un archivo CSV simple:
Puede deshacerse de una sola columna de muchas maneras; Usé la columna 2 como ejemplo. Probablemente sea la forma más fácil de usar cut
, lo que le permite especificar un delimitador -d
y qué campos desea imprimir -f
; esto le dice que se divida en comas y en el campo de salida 1 y los campos 3 hasta el final:
$ cut -d, -f1,3- /path/to/your/file
Si realmente necesita usar sed
, puede escribir una expresión regular que coincida con los primeros n-1
campos, el n
campo th y el resto, y omitir la salida n
th (aquí n
es 2, por lo que el primer grupo coincide con el 1
tiempo :) \{1\}
:
$ sed 's/\(\([^,]\+,\)\{1\}\)[^,]\+,\(.*\)/\1\3/' /path/to/your/file
Hay varias formas de hacerlo awk
, ninguna de ellas particularmente elegante. Puede usar un for
bucle, pero lidiar con la coma final es un dolor; ignorando que sería algo como:
$ awk -F, '{for(i=1; i<=NF; i++) if(i != 2) printf "%s,", $i; print NL}' /path/to/your/file
Me resulta más fácil generar el campo 1 y luego usarlo substr
para sacar todo después del campo 2:
$ awk -F, '{print $1 "," substr($0, length($1)+length($2)+3)}' /path/to/your/file
Sin embargo, esto es molesto para las columnas más adelante
En sed
esto es en esencia la misma expresión que antes, pero también se captura la columna de destino e incluir ese grupo varias veces en la sustitución:
$ sed 's/\(\([^,]\+,\)\{1\}\)\([^,]\+,\)\(.*\)/\1\3\3\4/' /path/to/your/file
En awk
la forma de bucle for, sería algo como (nuevamente ignorando la coma final):
$ awk -F, '{
for(i=1; i<=NF; i++) {
if(i == 2) printf "%s,", $i;
printf "%s,", $i
}
print NL
}' /path/to/your/file
El substr
camino:
$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file
(A tcdyl se le ocurrió un método mejor en su respuesta )
Creo que la sed
solución se deriva naturalmente de las demás, pero comienza a ser ridículamente larga.
awk
es tu mejor apuesta awk
imprime los campos por número, así que ...
awk 'BEGIN { FS=","; OFS=","; } {print $1,$2,$3}' file
Para eliminar una columna, no imprimirla:
awk 'BEGIN { FS=","; OFS=","; } {print $1,$3}' file
Para cambiar el orden:
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file
Redireccionar a un archivo de salida.
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file > output.file
awk
puede formatear la salida también.
Dado un archivo delimitado por espacios en el siguiente formato:
1 2 3 4 5
Puede eliminar el campo 2 con awk así:
awk '{ sub($2,""); print}' file
que vuelve
1 3 4 5
Reemplace la columna 2 con la columna n donde sea apropiado.
Para duplicar la columna 2,
awk '{ col = $2 " " $2; $2 = col; print }' file
que vuelve
1 2 2 3 4 5
Para cambiar las columnas 2 y 3,
awk '{temp = $2; $2 = $3; $3 = temp; print}'
que vuelve
1 3 2 4 5
awk es generalmente muy bueno para tratar el concepto de campos . Si está tratando con un archivo CSV, y no con un archivo delimitado por espacios, simplemente puede usar
awk -F,
para definir su campo como una coma, en lugar de un espacio (que es el valor predeterminado). Hay una serie de buenos recursos awk en línea, uno de los cuales enumero como fuente a continuación.
Fuente para # 3
awk
, pero parece que está separado por espacios, incluso si el separador de campo está ,
(el separador de campo solo controla cómo maneja la entrada)
Esto funcionará para eliminar
awk '{$2="";$0=$0;$1=$1}1'
Entrada
a b c d
Salida
a c d