¿Cómo analizar un archivo CSV en Bash?
Llegando tarde a esta pregunta y como bash ofrece nuevas funciones, porque esta pregunta se refiere a bash y porque ninguna de las respuestas ya publicadas muestra esta forma poderosa y compatible de hacer precisamente esto .
Análisis de archivos CSV en bash
, utilizando módulo cargable
Conforme a RFC 4180 , una cadena como esta muestra fila CSV :
12,22.45,"Hello, ""man"".","A, b.",42
debe dividirse como
1 12
2 22.45
3 Hello, "man".
4 A, b.
5 42
bash cargable Módulos compilados .C.
Bajo bash, puede crear, editar y usar módulos compilados c cargables . Una vez cargados, funcionan como cualquier otro incorporado !! (Puede encontrar más información en el árbol de fuentes.;)
El árbol de fuentes actual (15 de octubre de 2021, bash V5.1-rc3) contiene un montón de ejemplos:
accept listen for and accept a remote network connection on a given port
asort Sort arrays in-place
basename Return non-directory portion of pathname.
cat cat(1) replacement with no options - the way cat was intended.
csv process one line of csv data and populate an indexed array.
dirname Return directory portion of pathname.
fdflags Change the flag associated with one of bash's open file descriptors.
finfo Print file info.
head Copy first part of files.
hello Obligatory "Hello World" / sample loadable.
...
tee Duplicate standard input.
template Example template for loadable builtin.
truefalse True and false builtins.
tty Return terminal name.
uname Print system information.
unlink Remove a directory entry.
whoami Print out username of current user.
Hay un cvs
completamente funcional analizador listo para usar en examples/loadables
directorio:csv.c !!
En el sistema basado en Debian GNU/Linux, es posible que deba instalar el paquete bash-builtins por
apt install bash-builtins
Uso de bash-builds cargables :
Entonces:
enable -f /usr/lib/bash/csv csv
A partir de ahí, podrías usar csv
como un bash incorporado .
Con mi muestra:12,22.45,"Hello, ""man"".","A, b.",42
csv -a myArray '12,22.45,"Hello, ""man"".","A, b.",42'
printf "%s\n" "${myArray[@]}" | cat -n
1 12
2 22.45
3 Hello, "man".
4 A, b.
5 42
Luego, en un bucle, procesando un archivo.
while IFS= read -r line;do
csv -a aVar "$line"
printf "First two columns are: [ '%s' - '%s' ]\n" "${aVar[0]}" "${aVar[1]}"
done <myfile.csv
Esta forma es claramente la más rápida y más fuerte que usar cualquier otra combinación de bash incorporado o bifurcación de cualquier binario.
Desafortunadamente, dependiendo de la implementación de su sistema, si su versión de bash fue compilada sin loadable
, esto puede no funcionar...
Muestra completa con campos CSV de varias líneas.
Aquí hay un pequeño archivo de muestra con 1 titular, 4 columnas y 3 filas Porque dos campos contienen nueva línea , el archivo son 6 longitud de las líneas.
Id,Name,Desc,Value
1234,Cpt1023,"Energy counter",34213
2343,Sns2123,"Temperatur sensor
to trigg for alarm",48.4
42,Eye1412,"Solar sensor ""Day /
Night""",12199.21
Y un pequeño script capaz de analizar este archivo correctamente:
#!/bin/bash
enable -f /usr/lib/bash/csv csv
file="sample.csv"
exec {FD}<"$file"
read -ru $FD line
csv -a headline "$line"
printf -v fieldfmt '%-8s: "%%q"\\n' "${headline[@]}"
while read -ru $FD line;do
while csv -a row "$line" ; ((${#row[@]}<${#headline[@]})) ;do
read -ru $FD sline || break
line+=$'\n'"$sline"
done
printf "$fieldfmt\\n" "${row[@]}"
done
Esto puede generar:(he usado printf "%q"
para representar caracteres no imprimibles como nuevas líneas como $'\n'
)
Id : "1234"
Name : "Cpt1023"
Desc : "Energy\ counter"
Value : "34213"
Id : "2343"
Name : "Sns2123"
Desc : "$'Temperatur sensor\nto trigg for alarm'"
Value : "48.4"
Id : "42"
Name : "Eye1412"
Desc : "$'Solar sensor "Day /\nNight"'"
Value : "12199.21"
Puede encontrar una muestra de trabajo completa allí:csvsample.sh.txt orcsvsample.sh.
Advertencia:
¡Por supuesto, analizar CSV usando esto no es perfecto! ¡Esto funciona para muchos archivos CSV simples, pero se preocupa por la codificación y la seguridad! Por ejemplo, ¡este módulo no podrá manejar campos binarios!
¡Lea atentamente los comentarios del código fuente de csv.c y RFC 4180!
Podemos analizar archivos csv con cadenas entrecomilladas y delimitadas por decir | con el siguiente código
while read -r line
do
field1=$(echo "$line" | awk -F'|' '{printf "%s", $1}' | tr -d '"')
field2=$(echo "$line" | awk -F'|' '{printf "%s", $2}' | tr -d '"')
echo "$field1 $field2"
done < "$csvFile"
awk
analiza los campos de cadena a variables y tr
elimina la cita.
Ligeramente más lento que awk
se ejecuta para cada campo.
Del man
página:
-d delim El primer carácter de delim se usa para terminar la línea de entrada, en lugar de nueva línea.
Estás usando -d,
que terminará la línea de entrada en la coma. No leerá el resto de la línea. Por eso $y está vacío.
Necesitas usar IFS
en lugar de -d
:
while IFS=, read -r col1 col2
do
echo "I got:$col1|$col2"
done < myfile.csv
Tenga en cuenta que para el análisis de CSV de propósito general, debe usar una herramienta especializada que pueda manejar campos entrecomillados con comas internas, entre otros problemas que Bash no puede manejar por sí mismo. Ejemplos de tales herramientas son cvstool
y csvkit
.