Cómo extraer una columna de un archivo csv

111

Si tengo un archivo csv, ¿existe una forma rápida de bash para imprimir el contenido de una sola columna? Es seguro asumir que cada fila tiene el mismo número de columnas, pero el contenido de cada columna tendría una longitud diferente.

usuario788171
fuente

Respuestas:

136

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

awk -F "\"*,\"*" '{print $2}' textfile.csv
sintetizador
fuente
13
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'imprimirá en 2lugar de 2,3,4,5.
Igor Mikushkin
Si eres un tipo afortunado que usa las herramientas GNU en Windows, puedes ejecutar el mismo comando que @IgorMikushkin de la siguiente manera:gawk -F"|" "{print $13}" files*.csv
Elidio Marquina
10
Creo que esto falla cuando hay cadenas que contienen una coma, es decir...,"string,string",...
nitrato de sodio
Creo que para la primera y última columna, esto tendrá algún defecto. La primera columna comenzará con "y la última terminará con"
BigTailWolf
Algunos programas devuelven archivos CSV con diferentes delimitadores, por lo que puede ser necesario cambiar la expresión regular en consecuencia. Ejemplo de un delimitador de punto y coma: awk -F "\"*;\"*" '{print $2}' textfile.csv
gekkedev
88

si. cat mycsv.csv | cut -d ',' -f3imprimirá la tercera columna.

madrag
fuente
8
A menos que la columna dos contenga una coma, en cuyo caso obtendría la segunda mitad de la columna dos. Caso en el punto <col1>, "3,000", <col2>. Sin embargo, mi respuesta no es mucho mejor con respecto a ese problema. Así que no se desanime.
synthesizerpatel
@synthesizerpatel Estoy de acuerdo en usar mejorawk
MattSizzle
1
No estamos seguros de que su archivo CSV contenga comillas dobles para diferenciar los diferentes valores. Sería mejor que proporcione un archivo de entrada para que podamos evaluar la solución más adecuada.
Idriss Neumann
50

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

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

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

Samar
fuente
14
Esta debería ser la respuesta aceptada. Esta herramienta sabe cómo manejar archivos CSV, más allá de tratar una coma como un separador de campo. Para extraer la segunda columna, "csvtool col 2 input.csv"
Vladislavs Dovgalecs
3
Solo un aviso ... si desea usar csvtool con entrada estándar (ejemplo, csv proviene de otro comando) es algo como esto cat input.csv | csvtool formath '%(2)\n' -Nota Sé que cat here es inútil, pero sustitúyalo por cualquier comando que normalmente exportaría un csv.
General Redneck
Si hay campos de varias líneas, el format '%(2)\n'comando no podría decir dónde termina un campo. (csvtool 1.4.2)
jarno
1
Las versiones más nuevas csvtoolparecen requerir el uso -como nombre de archivo de entrada para leer desde stdin.
Connor Clark
@GeneralRedneck ¿por qué usar gato? y es formato, no formatocsvtool format '%(1),%(10)\n' - < in.csv > out.csv
sijanec
14

Aterrizado aquí buscando extraer de un archivo separado por pestañas. Pensé que agregaría.

cat textfile.tsv | cut -f2 -s

Donde -f2extrae la columna indexada 2, distinta de cero, o la segunda columna.

cevaris
fuente
simple, también el punto, y más fácilmente adaptable que los otros ejemplos. ¡Gracias!
Nick Jennings
6
Mentiroso, pero cates innecesario:< textfile.tsv cut -f2 -s
Anne van Rossum
8

Muchas respuestas para estas preguntas son excelentes y algunas incluso han examinado los casos de esquina. Me gustaría agregar una respuesta simple que puede ser de uso diario ... donde la mayoría de las veces se mete en esos casos de esquina (como haber salido comas o comas entre comillas, etc.).

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

Entonces, usando BEGIN (Ejecutar antes de tomar la entrada) podemos establecer este campo en lo que queramos ...

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

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

enrutador
fuente
1
He intentado esto y todavía considera las comas dentro de los campos entre comillas.
Daniel C. Sobral
5

Las otras respuestas funcionan bien, pero como solicitó una solución usando 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 extraer columnas (la primera en este ejemplo) así:

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

Entonces, hay un par de cosas que suceden aquí:

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

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

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

Ahí lo tienes, sacando una columna de un CSV en el shell. Las otras soluciones son probablemente más prácticas, pero esta es bash puro.

drldcsta
fuente
5

Puede utilizar GNU Awk, consulte este artículo de la guía del usuario . Como una mejora a la solución presentada en el artículo (en junio de 2015), el siguiente comando gawk permite comillas dobles dentro de campos entre 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 multilínea . El siguiente ejemplo imprime la tercera columna (vía 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 <textfile.csv)

Tenga en cuenta el uso de dos2unixpara convertir posibles saltos de línea de estilo DOS (CRLF, es decir, "\ r \ n") y codificación 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, consulte Wikipedia .

Si la entrada puede contener campos de varias líneas, puede utilizar el siguiente script. Tenga en cuenta el uso de una cadena especial para separar registros en la salida (ya que la nueva línea del separador predeterminado podría ocurrir dentro de un registro). Nuevamente, el siguiente ejemplo imprime la tercera columna (vía 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 <textfile.csv)

Hay otro enfoque del problema. csvquote puede generar contenido 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 se puedan utilizar para seleccionar cierta columna. Por ejemplo, el siguiente código genera la tercera columna:

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

csvquote se puede utilizar para procesar archivos grandes arbitrarios.

jarno
fuente
5

Aquí hay un ejemplo de archivo csv con 2 columnas

myTooth.csv

Date,Tooth
2017-01-25,wisdom
2017-02-19,canine
2017-02-24,canine
2017-02-28,wisdom

Para obtener la primera columna, use:

cut -d, -f1 myTooth.csv

f significa campo y d significa delimitador

Ejecutar el comando anterior producirá el siguiente resultado.

Salida

Date
2017-01-25
2017-02-19
2017-02-24
2017-02-28

Para obtener solo la segunda columna:

cut -d, -f2 myTooth.csv

Y aquí está la salida Salida

Tooth
wisdom
canine
canine
wisdom
incisor

Otro caso de uso:

Su archivo de entrada csv contiene 10 columnas y desea las columnas 2 a 5 y las columnas 8, usando una coma como separador ".

cut usa -f (que significa "campos") para especificar columnas y -d (que significa "delimitador") para especificar el separador. Debe especificar este último porque algunos archivos pueden usar espacios, tabulaciones o dos puntos para separar columnas.

cut -f 2-5,8 -d , myvalues.csv

cut es una utilidad de comando y aquí hay algunos ejemplos más:

SYNOPSIS
     cut -b list [-n] [file ...]
     cut -c list [file ...]
     cut -f list [-d delim] [-s] [file ...]
Stryker
fuente
4

Necesitaba un análisis CSV adecuado, no cut/ awky oración. Estoy probando esto en una Mac sin csvtool, pero las Mac vienen con ruby, así que puedes hacer:

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

Primero crearemos un CSV básico

[dumb@one pts]$ cat > file 
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

Entonces obtenemos la primera columna

[dumb@one pts]$  awk -F , '{print $1}' file  
a  
1  
a  
1
Raj Velayudhan
fuente
3
csvtool col 2 file.csv 

donde 2 es la columna que le interesa

tu tambien puedes hacer

csvtool col 1,2 file.csv 

hacer varias columnas

exussum
fuente
3

Creo que lo más fácil es usar csvkit :

Obtiene la segunda columna: csvcut -c 2 file.csv

Sin embargo, también existe csvtool , y probablemente una serie de otras herramientas bash csv por ahí:

sudo apt-get install csvtool (para sistemas basados ​​en Debian)

Esto devolvería una columna con la primera fila con 'ID' en ella. csvtool namedcol ID csv_file.csv

Esto devolvería la cuarta fila: csvtool col 4 csv_file.csv

Si desea eliminar la fila del encabezado:

csvtool col 4 csv_file.csv | sed '1d'

palabras por lo demás
fuente
2

Me pregunto por qué ninguna de las respuestas hasta ahora ha mencionado csvkit.

csvkit es un conjunto de herramientas de línea de comandos para convertir y trabajar con CSV

documentación csvkit

Lo uso exclusivamente para la gestión de datos csv y hasta ahora no he encontrado ningún problema que no haya podido resolver usando cvskit.

Para extraer una o más columnas de un archivo cvs, puede utilizar la csvcututilidad que forma parte de la caja de herramientas. Para extraer la segunda columna use este comando:

csvcut -c 2 filename_in.csv > filename_out.csv 

página de referencia de csvcut

Si las cadenas en el csv están entre comillas, agregue el carácter de comillas con la qopción:

csvcut -q '"' -c 2 filename_in.csv > filename_out.csv 

Instalar con pip install csvkito sudo apt install csvkit.

Soundbytes
fuente
1

No puede hacerlo sin un analizador CSV completo.

Peter Krumins
fuente
1
¿Cuándo cuenta algo como un analizador CSV completo? ¿ cutCuenta?
Hola
0

He estado usando 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 comas, guiones y tuberías 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
qneill
fuente
0

También puede usar while loop

IFS=,
while read name val; do
        echo "............................"

        echo Name: "$name"
done<itemlst.csv
K.Sopheak
fuente
Este código produce una advertencia de Shellcheck : SC2034 . La búsqueda devuelve esta pregunta como primer resultado al buscar formas de eludir la advertencia.
jww