Comment extraire une colonne d'un fichier csv
12 réponses
Vous pouvez utiliser awk pour cela. Changez '$ 2 ' à la nième colonne que vous voulez.
awk -F "\"*,\"*" '{print $2}' textfile.csv
La façon la plus simple d'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
Remplacer 2 par le numéro de colonne permet d'extraire efficacement les données de colonne que vous recherchez.
Atterri ici à la recherche d'extraire d'un fichier séparé par un onglet. Pensé que je pourrais ajouter.
cat textfile.tsv | cut -f2 -s
Où -f2
extrait la colonne indexée 2, non nulle, ou la deuxième colonne.
Les autres réponses fonctionnent bien, mais puisque 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 (les premières 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
Donc il y a quelques choses qui se passent ici:
while IFS=,
- Cela dit d'utiliser une virgule comme IFS (séparateur de champ interne), 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 le même que "A b" serait si IFS= " "(ce qui est ce qu'il est par défaut.)read -a csv_line;
- c'est dire lire chaque ligne, un à la fois et de créer un tableau où chaque élément est appelé "csv_line" et l'envoyer à la "section" de notre boucle whiledo echo "${csv_line[0]}";done < file
- Maintenant, nous sommes dans la phase" do", et nous disons echo le 0ème élément du tableau "csv_line". Cette action est répétée sur chaque ligne du fichier. La partie< file
indique simplement à la boucle while où Lire. NOTE: rappelez-vous, dans bash, les tableaux sont indexés à 0, donc la première colonne est le 0ème élément.
Donc là vous l'avez, en tirant une colonne d'un CSV dans le shell. Les autres solutions sont probablement plus pratiques, mais celle-ci est pure bash.
Beaucoup de réponses à ces questions sont grandes et certains ont même regardé dans les cas de coin. Je voudrais ajouter une réponse simple qui peut être d'usage quotidien... où vous entrez principalement dans ces cas de coin (comme avoir échappé des virgules ou des virgules entre guillemets, etc.,).
FS (séparateur de champs) est la variable dont la valeur est espace. Donc, awk par défaut se divise à l'espace pour n'importe quelle ligne.
Donc, en utilisant BEGIN (Execute avant de prendre input), 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.
[dumb@one pts] $ cat > file #Nous allons d'abord créer un fichier CSV de base
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
[muet@one pts]$ awk -F '{print $1}' fichier
un
1
un
1
Vous pouvez utiliser GNU Awk, voir cet article du guide de l'utilisateur.
Comme une amélioration de la solution présentée dans l'article (en juin 2015), la commande gawk suivante permet des guillemets doubles à l'intérieur des champs guillemets doubles; un guillemet double est marqué par deux guillemets doubles consécutifs ("") là. En outre, cela permet des champs vides, mais même cela ne peut pas gérer les champs multilignes . L'exemple suivant imprime la 3ème colonne (via c=3
) de fichier texte.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 le utilisation de dos2unix
pour convertir les sauts de ligne de style DOS possibles (CRLF, c'est-à-dire "\r\n") et l'encodage 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 (puisque le retour à la ligne du séparateur par défaut peut se produire dans un enregistrement). Encore une fois, les éléments suivants exemple imprime la 3ème colonne (via c=3
) de fichier texte.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 y a une autre approche du problème. csvquote {[9] } peut afficher le contenu D'un fichier CSV modifié afin 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.
J'avais besoin D'une analyse CSV correcte, pas cut
/ awk
et la prière. J'essaie cela sur un mac sans csvtool
, mais les Mac viennent avec ruby, donc vous pouvez faire:
echo "require 'csv'; CSV.read('new.csv').each {|data| puts data[34]}" | ruby
Vous ne pouvez pas le faire sans un analyseur CSV complet.
Utilise ce code depuis un moment, il n'est pas "rapide" sauf si vous comptez "couper et coller de stackoverflow".
Il utilise les opérateurs $ { # # } et $ { % % } dans une boucle au lieu de IFS. Il appelle ' err ' et 'die', et ne supporte que la virgule, le tiret et le tuyau 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
csvtool col 2 file.csv
Où 2 est la colonne qui vous intéresse
Vous pouvez également faire
csvtool col 1,2 file.csv
Pour faire plusieurs colonnes