Comment extraire une colonne d'un fichier csv


111

Si j'ai un fichier csv, existe-t-il un moyen rapide d'imprimer le contenu d'une seule colonne? Il est sûr de supposer que chaque ligne a le même nombre de colonnes, mais le contenu de chaque colonne aurait une longueur différente.

Réponses:


137

Vous pouvez utiliser awk pour cela. Remplacez «$ 2» par la nième colonne souhaitée.

awk -F "\"*,\"*" '{print $2}' textfile.csv

13
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'imprimera à la 2place de 2,3,4,5.
Igor Mikushkin

Si vous avez de la chance en utilisant les outils GNU dans Windows, vous pouvez exécuter la même commande que @IgorMikushkin comme suit:gawk -F"|" "{print $13}" files*.csv
Elidio Marquina

10
Je pense que cela échoue quand il y a des chaînes qui contiennent une virgule, c'est...,"string,string",...
sodiumnitrate

Je pense que pour le 1er et dernier colume, cela aura un défaut. La première colonne commencera par "et la dernière se terminera par"
BigTailWolf

Certains programmes renvoient des fichiers CSV avec des délimiteurs différents, il peut donc être nécessaire de modifier l'expression régulière en conséquence. Exemple de délimiteur de point-virgule: awk -F "\"*;\"*" '{print $2}' textfile.csv
gekkedev

88

Oui. cat mycsv.csv | cut -d ',' -f3imprimera la 3e colonne.


8
À moins que la deuxième colonne ne contienne une virgule, auquel cas vous obtiendrez la deuxième moitié de la deuxième colonne. Affaire au point <col1>, "3 000", <col2>. Ma réponse n'est cependant pas beaucoup mieux en ce qui concerne ce problème. Alors ne soyez pas déçu.
synthesizerpatel

@synthesizerpatel Je suis d'accord pour mieux utiliserawk
MattSizzle

1
Nous ne sommes pas sûrs que son fichier CSV contienne des guillemets doubles pour différencier les différentes valeurs. Il vaudrait mieux qu'il fournisse un fichier d'entrée afin que nous puissions évaluer la solution la plus appropriée.
Idriss Neumann

51

Le moyen le plus simple pour y parvenir était d'utiliser simplement csvtool . J'ai également eu d'autres cas d'utilisation pour utiliser csvtool et il peut gérer les guillemets ou les délimiteurs de manière appropriée s'ils apparaissent dans les données de la colonne elle-même.

csvtool format '%(2)\n' input.csv

Le remplacement de 2 par le numéro de colonne extraira efficacement les données de colonne que vous recherchez.


14
Cela devrait être la réponse acceptée. Cet outil sait comment gérer les fichiers CSV, bien au-delà de traiter une virgule comme un séparateur de champ. Pour extraire la 2ème colonne, "csvtool col 2 input.csv"
Vladislavs Dovgalecs

3
Juste un avertissement ... si vous voulez utiliser csvtool avec une entrée standard (par exemple, csv provient d'une autre commande), c'est quelque chose comme ça cat input.csv | csvtool formath '%(2)\n' -.
General Redneck

S'il y a des champs multilignes, la format '%(2)\n'commande ne peut pas dire où se termine un champ. (csvtool 1.4.2)
jarno

1
Les nouvelles versions de csvtoolsemblent nécessiter l'utilisation -comme nom de fichier d'entrée pour lire depuis stdin.
Connor Clark

@GeneralRedneck pourquoi utiliser cat? et son format n'est pas formathcsvtool format '%(1),%(10)\n' - < in.csv > out.csv
sijanec

15

Atterri ici à la recherche d'extraits d'un fichier séparé par des tabulations. Je pensais que j'ajouterais.

cat textfile.tsv | cut -f2 -s

-f2extrait la colonne indexée 2 non nulle ou la deuxième colonne.


simple, aussi le point, et plus facilement adaptable que les autres exemples. Merci!
Nick Jennings

6
Nitpicking, mais catc'est inutile:< textfile.tsv cut -f2 -s
Anne van Rossum

8

De nombreuses réponses à ces questions sont excellentes et certaines ont même examiné les cas secondaires. Je voudrais ajouter une réponse simple qui peut être d'un usage quotidien ... où vous entrez principalement dans ces cas d'angle (comme avoir échappé des virgules ou des virgules entre guillemets, etc.).

FS (Field Separator) est la variable dont la valeur est par défaut en espace. Donc awk par défaut se divise en espace pour n'importe quelle ligne.

Donc, en utilisant BEGIN (Exécuter avant de prendre une entrée), nous pouvons définir ce champ sur tout ce que nous voulons ...

awk 'BEGIN {FS = ","}; {print $3}'

Le code ci-dessus imprimera la 3ème colonne dans un fichier csv.


1
J'ai essayé ceci, et il considère toujours les virgules dans les champs entre guillemets.
Daniel C. Sobral

5

Les autres réponses fonctionnent bien, mais comme vous avez demandé une solution en utilisant uniquement le shell bash, vous pouvez le faire:

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

Et puis vous pouvez extraire des colonnes (la première dans cet exemple) comme ceci:

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

Il se passe donc deux ou trois choses ici:

  • while IFS=,- cela veut dire utiliser une virgule comme IFS (Internal Field Separator), qui est ce que le shell utilise pour savoir ce qui sépare les champs (blocs de texte). Donc dire IFS =, c'est comme dire "a, b" est la même chose que "a b" le serait si IFS = "" (ce qui est ce que c'est par défaut).

  • read -a csv_line; - cela signifie lire dans chaque ligne, une à la fois et créer un tableau où chaque élément est appelé "csv_line" et l'envoyer à la section "do" de notre boucle while

  • do echo "${csv_line[0]}";done < file- maintenant nous sommes dans la phase "faire", et nous disons echo le 0e élément du tableau "csv_line". Cette action est répétée sur chaque ligne du fichier. La < filepartie indique simplement à la boucle while où lire. REMARQUE: rappelez-vous que dans bash, les tableaux sont indexés à 0, donc la première colonne est le 0e élément.

Donc là vous l'avez, extraire une colonne d'un CSV dans le shell. Les autres solutions sont probablement plus pratiques, mais celle-ci est pure bash.


5

Vous pouvez utiliser GNU Awk, voir cet article du guide de l'utilisateur . Pour améliorer la solution présentée dans l'article (en juin 2015), la commande gawk suivante autorise les guillemets à l'intérieur des champs entre guillemets doubles; un guillemet double y est marqué par deux guillemets doubles consécutifs (""). De plus, cela autorise les champs vides, mais même cela ne peut pas gérer les champs multilignes . L'exemple suivant imprime la 3e colonne (via 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)

Notez l'utilisation de dos2unixpour convertir les sauts de ligne de style DOS possibles (CRLF c'est-à-dire "\ r \ n") et le codage UTF-16 (avec marque d'ordre d'octet) en "\ n" et UTF-8 (sans marque d'ordre d'octet), respectivement. Les fichiers CSV standard utilisent CRLF comme saut de ligne, voir Wikipedia .

Si l'entrée peut contenir des champs multilignes, vous pouvez utiliser le script suivant. Notez l'utilisation d'une chaîne spéciale pour séparer les enregistrements en sortie (car le saut de ligne par défaut du séparateur peut se produire dans un enregistrement). Encore une fois, l'exemple suivant imprime la 3e colonne (via 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)

Il existe une autre approche du problème. csvquote peut afficher le contenu d'un fichier CSV modifié de sorte que les caractères spéciaux dans le champ soient transformés afin que les outils de traitement de texte Unix habituels puissent être utilisés pour sélectionner certaines colonnes. Par exemple, le code suivant génère la troisième colonne:

csvquote textfile.csv | cut -d ',' -f 3 | csvquote -u

csvquote peut être utilisé pour traiter des fichiers volumineux arbitraires.


5

Voici un exemple de fichier csv avec 2 colonnes

myTooth.csv

Date,Tooth
2017-01-25,wisdom
2017-02-19,canine
2017-02-24,canine
2017-02-28,wisdom

Pour obtenir la première colonne, utilisez:

cut -d, -f1 myTooth.csv

f représente le champ et d le délimiteur

L'exécution de la commande ci-dessus produira la sortie suivante.

Production

Date
2017-01-25
2017-02-19
2017-02-24
2017-02-28

Pour obtenir la 2ème colonne uniquement:

cut -d, -f2 myTooth.csv

Et voici la sortie Output

Tooth
wisdom
canine
canine
wisdom
incisor

Un autre cas d'utilisation:

Votre fichier d'entrée csv contient 10 colonnes et vous voulez les colonnes 2 à 5 et les colonnes 8, en utilisant la virgule comme séparateur ".

cut utilise -f (signifiant «champs») pour spécifier les colonnes et -d (signifiant «délimiteur») pour spécifier le séparateur. Vous devez spécifier ce dernier car certains fichiers peuvent utiliser des espaces, des tabulations ou des deux-points pour séparer les colonnes.

cut -f 2-5,8 -d , myvalues.csv

cut est un utilitaire de commande et voici quelques exemples supplémentaires:

SYNOPSIS
     cut -b list [-n] [file ...]
     cut -c list [file ...]
     cut -f list [-d delim] [-s] [file ...]

4

J'avais besoin d'une analyse CSV appropriée, pas de cut/ awket de prière. J'essaye ceci sur un mac sans csvtool, mais les macs sont livrés avec ruby, donc vous pouvez faire:

echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | ruby

4

Nous allons d'abord créer un CSV de base

[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

Ensuite, nous obtenons la 1ère colonne

[dumb@one pts]$  awk -F , '{print $1}' file  
a  
1  
a  
1

3
csvtool col 2 file.csv 

où 2 est la colonne qui vous intéresse

tu peux aussi faire

csvtool col 1,2 file.csv 

faire plusieurs colonnes


3

Je pense que le plus simple est d'utiliser csvkit :

Obtient la 2ème colonne: csvcut -c 2 file.csv

Cependant, il existe aussi csvtool , et probablement un certain nombre d'autres outils csv bash:

sudo apt-get install csvtool (pour les systèmes basés sur Debian)

Cela renverrait une colonne avec la première ligne contenant «ID». csvtool namedcol ID csv_file.csv

Cela renverrait la quatrième ligne: csvtool col 4 csv_file.csv

Si vous souhaitez supprimer la ligne d'en-tête:

csvtool col 4 csv_file.csv | sed '1d'


2

Je me demande pourquoi aucune des réponses jusqu'à présent n'a mentionné csvkit.

csvkit est une suite d'outils de ligne de commande pour la conversion et l'utilisation de CSV

documentation csvkit

Je l'utilise exclusivement pour la gestion des données csv et jusqu'à présent je n'ai pas trouvé de problème que je ne pourrais pas résoudre en utilisant cvskit.

Pour extraire une ou plusieurs colonnes d'un fichier cvs, vous pouvez utiliser l' csvcututilitaire qui fait partie de la boîte à outils. Pour extraire la deuxième colonne, utilisez cette commande:

csvcut -c 2 filename_in.csv > filename_out.csv 

page de référence csvcut

Si les chaînes du csv sont entre guillemets, ajoutez le caractère guillemet avec l' qoption:

csvcut -q '"' -c 2 filename_in.csv > filename_out.csv 

Installez avec pip install csvkitou sudo apt install csvkit.



0

J'utilise ce code depuis un moment, ce n'est pas "rapide" à moins que vous ne comptiez "couper-coller depuis stackoverflow".

Il utilise les opérateurs $ {##} et $ {%%} dans une boucle au lieu d'IFS. Il appelle «err» et «die», et ne prend en charge que la virgule, le tiret et le tube en tant que caractères SEP (c'est tout ce dont j'avais besoin).

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*}
}

Exemple:

$ 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

0

Vous pouvez également utiliser la boucle while

IFS=,
while read name val; do
        echo "............................"

        echo Name: "$name"
done<itemlst.csv

Ce code produit un avertissement Shellcheck : SC2034 . La recherche renvoie cette question comme premier résultat lors de la recherche de moyens de contourner l'avertissement.
jww
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.