Comment analyser un fichier CSV dans Bash?

112

Je travaille sur un long script Bash. Je veux lire les cellules d'un fichier CSV dans des variables Bash. Je peux analyser les lignes et la première colonne, mais pas toute autre colonne. Voici mon code pour l'instant:


  cat myfile.csv|while read line
  do
    read -d, col1 col2 < <(echo $line)
    echo "I got:$col1|$col2"
  done

Il n'imprime que la première colonne. Comme test supplémentaire, j'ai essayé ce qui suit:

read -d, x y < <(echo a,b,)

Et $ y est vide. Alors j'ai essayé:

read x y < <(echo a b)

Et $ y est b. Pourquoi?

Utilisateur1
la source
7
avez - vous pensé awkà utiliser $1, $2etc?
BeemerGuy
4
comme note latérale: commande <<(echo "string") ---> commande <<< "string"
tokland
1
Le programme de ligne de commande 'cut' a été conçu pour cela: ss64.com/bash/cut.html
Jay

Réponses:

215

Vous devez utiliser IFSau lieu de -d:

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

Notez que pour l'analyse CSV à usage général, vous devez utiliser un outil spécialisé qui peut 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 cvstoolet csvkit.

Suspendu jusqu'à nouvel ordre.
la source
7
La solution proposée convient aux fichiers CSV très simples, c'est-à-dire si les en-têtes et les valeurs sont exempts de virgules et de guillemets incorporés. Il est en fait assez délicat d'écrire un analyseur CSV générique (d'autant plus qu'il existe plusieurs "standards" CSV). Une approche pour rendre les fichiers CSV plus accessibles aux outils * nix consiste à les convertir en TSV (valeurs séparées par des tabulations), par exemple en utilisant Excel.
pic le
Il est intéressant de noter que je ne peux pas faire mkdir dans le corps. Je reçois command not found. Seuls les echotravaux.
Zsolt
1
@Zsolt: Il n'y a aucune raison que ce soit le cas. Vous devez avoir une faute de frappe ou un caractère non imprimable errant.
Suspendu jusqu'à nouvel ordre.
2
@DennisWilliamson Vous devez inclure le séparateur, par exemple lorsque vous utilisez ;:while IFS=";" read col1 col2; do ...
thomas.mc.work
1
@ thomas.mc.work: C'est vrai dans le cas des points-virgules et autres caractères spéciaux pour le shell. Dans le cas d'une virgule, ce n'est pas nécessaire et j'ai tendance à préférer omettre les caractères inutiles. Par exemple, vous pouvez toujours spécifier des variables pour l'expansion en utilisant des accolades (par exemple ${var}), mais je les omette lorsqu'elles ne sont pas nécessaires. Pour moi, ça a l'air plus propre.
Suspendu jusqu'à nouvel ordre.
10

Depuis la manpage:

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

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

dogbane
la source
3

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 le guillemet.

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

Maithilish
la source
1
Bon, vous pouvez aussi utiliser coma (,)
pkarc
0

Si vous voulez lire un fichier CSV avec quelques lignes, voici la solution.

while IFS=, read -ra line
do 
    test $i -eq 1 && ((i=i+1)) && continue
    for col_val in ${line[@]}
    do
        echo -n "$col_val|"                 
    done
    echo        
done < "$csvFile"
Eliya
la source