GNU/Linux >> Tutoriels Linux >  >> Linux

Comment parser un fichier CSV dans Bash ?

Comment analyser un fichier CSV dans Bash ?

Arriver en retard à cette question et comme bash offre de nouvelles fonctionnalités, parce que cette question porte sur bash et parce qu'aucune des réponses déjà publiées ne montre cette façon puissante et conforme de faire précisément cela .

Analyse des fichiers CSV sous bash , en utilisant le module chargeable

Conforme à la RFC 4180 , une chaîne comme cet exemple de ligne CSV :

12,22.45,"Hello, ""man"".","A, b.",42

doit être divisé comme

 1  12
 2  22.45
 3  Hello, "man".
 4  A, b.
 5  42

bash chargeable Modules compilés en .C.

Sous bash, vous pouvez créer, modifier et utiliser des modules compilés c chargeables . Une fois chargés, ils fonctionnent comme n'importe quel autre intégré !! (Vous pouvez trouver plus d'informations dans l'arborescence source.;)

L'arborescence source actuelle (15 octobre 2021, bash V5.1-rc3) contient un tas d'exemples :

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.

Il existe un cvs complet qui fonctionne analyseur prêt à l'emploi en examples/loadables répertoire :csv.c !!

Sous le système basé sur Debian GNU/Linux, vous devrez peut-être installer le paquet bash-builtins par

apt install bash-builtins

Utilisation bash-builtins chargeables :

Ensuite :

enable -f /usr/lib/bash/csv csv

À partir de là, vous pouvez utiliser csv en tant que bash intégré .

Avec mon échantillon :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

Puis en boucle, traitement d'un fichier.

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

Cette méthode est clairement la plus rapide et la plus puissante que l'utilisation de toute autre combinaison de commandes intégrées bash ou d'un fork vers n'importe quel binaire.

Malheureusement, selon l'implémentation de votre système, si votre version de bash a été compilée sans loadable , cela peut ne pas fonctionner...

Échantillon complet avec des champs CSV multilignes.

Voici un petit exemple de fichier avec 1 titre, 4 colonnes et 3 Lignes. Parce que deux champs contiennent newline , le fichier est 6 longueur des lignes.

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

Et un petit script capable d'analyser correctement ce fichier :

#!/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

Cela peut rendre :(j'ai utilisé printf "%q" pour représenter des caractères non imprimables comme newlines comme $'\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"

Vous pouvez y trouver un exemple de travail complet :csvsample.sh.txt orcsvsample.sh.

Avertissement :

Bien sûr, l'analyse CSV à l'aide de this n'est pas parfaite ! Cela fonctionne pour de nombreux fichiers CSV simples, mais attention à l'encodage et à la sécurité !! Par exemple, ce module ne pourra pas gérer les champs binaires !

Lisez attentivement les commentaires du code source csv.c et la RFC 4180 !


Nous pouvons analyser les fichiers csv avec des chaînes entre guillemets et délimités par say | avec le code suivant

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 analyse les champs de chaîne en variables et tr supprime la citation.

Légèrement plus lent que awk est exécuté pour chaque champ.


Du man page :

-d delimLe premier caractère de delim est utilisé pour terminer la ligne d'entrée, plutôt que la nouvelle ligne.

Vous utilisez -d, qui terminera la ligne d'entrée sur la virgule. Il ne lira pas le reste de la ligne. C'est pourquoi $y est vide.


Vous devez utiliser IFS au lieu de -d :

while IFS=, read -r col1 col2
do
    echo "I got:$col1|$col2"
done < myfile.csv

Notez que pour une analyse CSV à usage général, vous devez utiliser un outil spécialisé capable de gérer les champs entre guillemets avec des virgules internes, entre autres problèmes que Bash ne peut pas gérer par lui-même. Des exemples de tels outils sont cvstool et csvkit .


Linux
  1. Comment mettre en surbrillance les scripts Bash dans Vim ?

  2. Comment inclure un fichier dans un script shell bash

  3. Comment obtenir le répertoire absolu d'un fichier dans bash ?

  4. Comment grep \n dans le fichier

  5. Comment analyser les en-têtes HTTP à l'aide de Bash ?

Comment vérifier si un fichier ou un répertoire existe dans Bash

Comment lire un fichier ligne par ligne dans Bash

Comment rediriger stderr vers stdout dans Bash

Comment utiliser les opérateurs de test de fichiers Bash sous Linux

Comment analyser les fichiers CSV dans les scripts Bash sous Linux

Comment vérifier si un fichier ou un répertoire existe dans Bash Shell