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.
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.
Respuestas:
Podrías usar awk para esto. Cambie '$ 2' a la enésima columna que desee.
awk -F "\"*,\"*" '{print $2}' textfile.csv
gawk -F"|" "{print $13}" files*.csv
...,"string,string",...
"
y la última terminará con"
awk -F "\"*;\"*" '{print $2}' textfile.csv
si. cat mycsv.csv | cut -d ',' -f3
imprimirá la tercera columna.
awk
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.
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.
format '%(2)\n'
comando no podría decir dónde termina un campo. (csvtool 1.4.2)
csvtool
parecen requerir el uso -
como nombre de archivo de entrada para leer desde stdin.
csvtool format '%(1),%(10)\n' - < in.csv > out.csv
Aterrizado aquí buscando extraer de un archivo separado por pestañas. Pensé que agregaría.
cat textfile.tsv | cut -f2 -s
Donde -f2
extrae la columna indexada 2, distinta de cero, o la segunda columna.
cat
es innecesario:< textfile.tsv cut -f2 -s
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.
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 < file
parte 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.
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 dos2unix
para 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.
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 ...]
Necesitaba un análisis CSV adecuado, no cut
/ awk
y 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
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
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'
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
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 csvcut
utilidad 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 q
opción:
csvcut -q '"' -c 2 filename_in.csv > filename_out.csv
Instalar con pip install csvkit
o sudo apt install csvkit
.
No puede hacerlo sin un analizador CSV completo.
cut
Cuenta?
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
También puede usar while loop
IFS=,
while read name val; do
echo "............................"
echo Name: "$name"
done<itemlst.csv
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'
imprimirá en2
lugar de2,3,4,5
.