GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo analizar un archivo CSV en Bash?

¿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 .


Linux
  1. ¿Cómo verificar syslog en Bash en Linux?

  2. ¿Cómo verificar si un archivo está vacío en Bash?

  3. ¿Cómo obtengo el directorio absoluto de un archivo en bash?

  4. Cómo grep \n en el archivo

  5. ¿Cómo analizar encabezados HTTP usando Bash?

Cómo verificar si existe un archivo o directorio en Bash

Cómo leer un archivo línea por línea en Bash

Cómo redirigir stderr a stdout en Bash

Cómo usar operadores de prueba de archivos Bash en Linux

Cómo analizar archivos CSV en Bash Scripts en Linux

Cómo verificar si existe un archivo o directorio en Bash Shell