Cómo extraer una columna de un archivo csv

Si tengo un archivo csv, ¿hay una manera rápida de imprimir los contenidos de solo una columna? Es seguro suponer que cada fila tiene el mismo número de columnas, pero el contenido de cada columna tendría una longitud diferente.

Podrías usar awk para esto. Cambie ‘$ 2’ a la enésima columna que desee.

awk -F "\"*,\"*" '{print $2}' textfile.csv 

sí. cat mycsv.csv | cut -d ',' -f3 cat mycsv.csv | cut -d ',' -f3 imprimirá la tercera columna.

La forma más sencilla en que pude hacer esto fue simplemente usar csvtool . También tuve otros casos de uso para usar csvtool y puede manejar las comillas o los delimitadores de manera apropiada si aparecen dentro de los datos de la columna en sí.

 csvtool format '%(2)\n' input.csv 

Reemplazar 2 con el número de columna extraerá efectivamente los datos de columna que está buscando.

Aterrizó aquí buscando extraer de un archivo separado por tabs. Pensé que agregaría.

 cat textfile.tsv | cut -f2 -s 

Donde -f2 extrae el 2, columna indexada distinta de cero, o la segunda columna.

Las otras respuestas funcionan bien, pero como pidió una solución utilizando solo el shell bash, puede hacer esto:

 AirBoxOmega:~ d$ cat > file #First we'll create a basic CSV a,b,c,d,e,f,g,h,i,k 1,2,3,4,5,6,7,8,9,10 a,b,c,d,e,f,g,h,i,k 1,2,3,4,5,6,7,8,9,10 a,b,c,d,e,f,g,h,i,k 1,2,3,4,5,6,7,8,9,10 a,b,c,d,e,f,g,h,i,k 1,2,3,4,5,6,7,8,9,10 a,b,c,d,e,f,g,h,i,k 1,2,3,4,5,6,7,8,9,10 a,b,c,d,e,f,g,h,i,k 1,2,3,4,5,6,7,8,9,10 

Y luego puede sacar las columnas (la primera en este ejemplo) como sigue:

 AirBoxOmega:~ d$ while IFS=, read -a csv_line;do echo "${csv_line[0]}";done < file a 1 a 1 a 1 a 1 a 1 a 1 

Así que hay un par de cosas pasando aquí:

  • while IFS=, - esto significa usar una coma como el IFS (Separador interno de campos), que es lo que el shell usa para saber qué separa los campos (bloques de texto). Así que decir IFS =, es como decir "a, b" es lo mismo que "ab" sería si IFS = "" (que es lo que es por defecto).

  • read -a csv_line; - esto significa leer en cada línea, una a la vez y crear una matriz donde cada elemento se llama "csv_line" y enviarlo a la sección "do" de nuestro ciclo while

  • do echo "${csv_line[0]}";done < file - ahora estamos en la fase "do", y estamos diciendo que echo el 0 ° elemento de la matriz "csv_line". Esta acción se repite en cada línea del archivo. La < file parte de < file está diciendo al ciclo while de dónde leer. NOTA: recuerde, en bash, las matrices están indexadas en 0, por lo que la primera columna es el 0 ° elemento.

Entonces ahí lo tienes, sacando una columna de un CSV en el caparazón. Las otras soluciones son probablemente más prácticas, pero esta es pura bash.

Muchas respuestas para estas preguntas son excelentes y algunas incluso han investigado los casos de las esquinas. Me gustaría agregar una respuesta simple que pueda ser de uso diario … donde en su mayoría ingresas en esos casos de esquina (como haber escapado de comas o comas entre comillas, etc.).

FS (Separador de campo) es la variable cuyo valor se dafaulted al espacio. Entonces awk por defecto se divide en el espacio para cualquier línea.

Entonces, al usar BEGIN (Ejecutar antes de tomar entrada) podemos establecer este campo a cualquier cosa que queramos …

 awk 'BEGIN {FS = ","}; {print $3}' 

El código anterior imprimirá la tercera columna en un archivo csv.

[dumb @ one pts] $ cat> file # Primero, crearemos un CSV básico
a, b, c, d, e, f, g, h, i, k
1,2,3,4,5,6,7,8,9,10
a, b, c, d, e, f, g, h, i, k
1,2,3,4,5,6,7,8,9,10

[dumb @ one pts] $ awk -F, ‘{print $ 1}’ archivo
un
1
un
1

Puede usar GNU Awk, consulte este artículo de la guía del usuario . Como una mejora de la solución presentada en el artículo (en junio de 2015), el siguiente comando gawk permite comillas dobles dentro de los campos con comillas dobles; una comilla doble está marcada por dos comillas dobles consecutivas (“”) allí. Además, esto permite campos vacíos, pero incluso esto no puede manejar campos de líneas múltiples . El siguiente ejemplo imprime la tercera columna (a través de c=3 ) de textfile.csv:

 #!/bin/bash gawk -- ' BEGIN{ FPAT="([^,\"]*)|(\"((\"\")*[^\"]*)*\")" } { if (substr($c, 1, 1) == "\"") { $c = substr($c, 2, length($c) - 2) # Get the text within the two quotes gsub("\"\"", "\"", $c) # Normalize double quotes } print $c } ' c=3 < <(dos2unix  

Tenga en cuenta el uso de dos2unix para convertir posibles saltos de línea estilo DOS (CRLF es decir "\ r \ n") y encoding UTF-16 (con marca de orden de bytes) a "\ n" y UTF-8 (sin marca de orden de bytes), respectivamente . Los archivos CSV estándar usan CRLF como salto de línea, ver Wikipedia .

Si la entrada puede contener campos de líneas múltiples, puede usar la siguiente secuencia de comandos. Tenga en cuenta el uso de una cadena especial para separar registros en la salida (ya que la nueva línea separador predeterminada podría ocurrir dentro de un registro). Nuevamente, el siguiente ejemplo imprime la tercera columna (a través de c=3 ) de textfile.csv:

 #!/bin/bash gawk -- ' BEGIN{ RS="\0" # Read the whole input file as one record; # assume there is no null character in input. FS="" # Suppose this setting eases internal splitting work. ORS="\n####\n" # Use a special output separator to show borders of a record. } { nof=patsplit($0, a, /([^,"\n]*)|("(("")*[^"]*)*")/, seps) field=0; for (i=1; i<=nof; i++){ field++ if (field==c) { if (substr(a[i], 1, 1) == "\"") { a[i] = substr(a[i], 2, length(a[i]) - 2) # Get the text within # the two quotes. gsub(/""/, "\"", a[i]) # Normalize double quotes. } print a[i] } if (seps[i]!=",") field=0 } } ' c=3 < <(dos2unix  

Hay otro acercamiento al problema. csvquote puede generar contenidos de salida de un archivo CSV modificado para que los caracteres especiales dentro del campo se transformen para que las herramientas habituales de procesamiento de texto de Unix puedan utilizarse para seleccionar determinada columna. Por ejemplo, el siguiente código genera la tercera columna:

 csvquote textfile.csv | cut -d ',' -f 3 | csvquote -u 

csvquote se puede usar para procesar archivos grandes arbitrarios.

Necesitaba un análisis CSV correcto, no cut / awk y oración. Estoy intentando esto en un mac sin csvtool , pero los Mac vienen con ruby, así que puedes hacer:

 echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | ruby 

No puedes hacerlo sin un analizador de CSV completo.

Utilizando este código por un tiempo, no es “rápido” a menos que cuente “cortar y pegar desde stackoverflow”.

Utiliza operadores $ {##} y $ {%%} en un bucle en lugar de IFS. Llama ‘err’ y ‘die’, y solo admite coma, guión y pipa como caracteres SEP (eso es todo lo que necesitaba).

 err() { echo "${0##*/}: Error:" "$@" >&2; } die() { err "$@"; exit 1; } # Return Nth field in a csv string, fields numbered starting with 1 csv_fldN() { fldN , "$1" "$2"; } # Return Nth field in string of fields separated # by SEP, fields numbered starting with 1 fldN() { local me="fldN: " local sep="$1" local fldnum="$2" local vals="$3" case "$sep" in -|,|\|) ;; *) die "$me: arg1 sep: unsupported separator '$sep'" ;; esac case "$fldnum" in [0-9]*) [ "$fldnum" -gt 0 ] || { err "$me: arg2 fldnum=$fldnum must be number greater or equal to 0."; return 1; } ;; *) { err "$me: arg2 fldnum=$fldnum must be number"; return 1;} ;; esac [ -z "$vals" ] && err "$me: missing arg2 vals: list of '$sep' separated values" && return 1 fldnum=$(($fldnum - 1)) while [ $fldnum -gt 0 ] ; do vals="${vals#*$sep}" fldnum=$(($fldnum - 1)) done echo ${vals%%$sep*} } 

Ejemplo:

 $ CSVLINE="example,fields with whitespace,field3" $ $ for fno in $(seq 3); do echo field$fno: $(csv_fldN $fno "$CSVLINE"); done field1: example field2: fields with whitespace field3: field3 
 csvtool col 2 file.csv 

donde 2 es la columna que le interesa

también puedes hacer

 csvtool col 1,2 file.csv 

hacer múltiples columnas