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.
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:
Vous pouvez utiliser awk pour cela. Remplacez «$ 2» par la nième colonne souhaitée.
awk -F "\"*,\"*" '{print $2}' textfile.csv
gawk -F"|" "{print $13}" files*.csv
...,"string,string",...
"
et la dernière se terminera par"
awk -F "\"*;\"*" '{print $2}' textfile.csv
Oui. cat mycsv.csv | cut -d ',' -f3
imprimera la 3e colonne.
awk
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.
cat input.csv | csvtool formath '%(2)\n' -
.
format '%(2)\n'
commande ne peut pas dire où se termine un champ. (csvtool 1.4.2)
csvtool
semblent nécessiter l'utilisation -
comme nom de fichier d'entrée pour lire depuis stdin.
csvtool format '%(1),%(10)\n' - < in.csv > out.csv
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
Où -f2
extrait la colonne indexée 2 non nulle ou la deuxième colonne.
cat
c'est inutile:< textfile.tsv cut -f2 -s
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.
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 < file
partie 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.
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 dos2unix
pour 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.
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 ...]
J'avais besoin d'une analyse CSV appropriée, pas de cut
/ awk
et 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
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
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'
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
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' csvcut
utilitaire 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
Si les chaînes du csv sont entre guillemets, ajoutez le caractère guillemet avec l' q
option:
csvcut -q '"' -c 2 filename_in.csv > filename_out.csv
Installez avec pip install csvkit
ou sudo apt install csvkit
.
Vous ne pouvez pas le faire sans un analyseur CSV complet.
cut
compte?
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
Vous pouvez également utiliser la boucle while
IFS=,
while read name val; do
echo "............................"
echo Name: "$name"
done<itemlst.csv
echo '1,"2,3,4,5",6' | awk -F "\"*,\"*" '{print $2}'
imprimera à la2
place de2,3,4,5
.