Si j'ai un fichier csv, existe-t-il un moyen rapide en bash d'imprimer le contenu d'une seule colonne? Il est prudent de supposer que chaque ligne contient le même nombre de colonnes, mais que le contenu de chaque colonne aura une longueur différente.
Vous pouvez utiliser awk pour cela. Remplacez '$ 2' par la nième colonne souhaitée.
awk -F "\"*,\"*" '{print $2}' textfile.csv
oui. cat mycsv.csv | cut -d ',' -f3
imprimera la 3ème colonne.
La façon la plus simple de réaliser cela était d’utiliser simplement csvtool . J'avais également d'autres cas d'utilisation pour utiliser csvtool et celui-ci peut gérer les guillemets ou les délimiteurs de manière appropriée s'ils apparaissent dans les données de colonne elles-mêmes.
csvtool format '%(2)\n' input.csv
Si vous remplacez 2 par le numéro de colonne, les données de colonne recherchées seront extraites.
A atterri ici cherchant à extraire d'un fichier séparé par des tabulations. Je pensais ajouter.
cat textfile.tsv | cut -f2 -s
Où -f2
extrait les 2 ou la deuxième colonne indexée, différente de zéro.
Beaucoup de réponses à ces questions sont excellentes et certaines ont même jeté un œil dans les cas les plus critiques. Je voudrais ajouter une réponse simple qui peut être d'usage quotidien ... où vous tombez principalement dans ces virages (comme avoir échappé des virgules ou des virgules, etc.).
FS (Field Separator) est la variable dont la valeur est définie sur 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 à 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 puisque vous avez demandé une solution en utilisant uniquement le bash Shell, 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 y a donc deux choses qui se passent ici:
while IFS=,
- Cela veut dire utiliser une virgule comme IFS (séparateur de champs internes), 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 identique à "a b" serait si IFS = "" (ce qui est ce qu'il est par défaut).
read -a csv_line;
- Ceci dit de lire dans chaque ligne, une à la fois, et de créer un tableau où chaque élément est appelé "csv_line" et de l'envoyer à la section "do" de notre boucle while
do echo "${csv_line[0]}";done < file
- nous sommes maintenant dans la phase "do" et nous disons que echo est 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 que dans bash, les tableaux ont la valeur 0 indexé, la première colonne est donc le 0e élément.
Vous avez donc maintenant extrait une colonne d'un fichier 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 . En guise d'amélioration de la solution présentée dans l'article (en juin 2015), la commande suivante de gawk permet d'utiliser des guillemets doubles. champs cités; un guillemet double est marqué par deux guillemets consécutifs (""). De plus, cela permet aux champs vides, mais même cela ne peut pas gérer les champs multilignes. L'exemple suivant affiche la 3ème 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 des octets) en "\ n" et UTF-8 (sans marque d'ordre des octets), 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 dans la sortie (car le retour à la ligne par défaut du séparateur pourrait se produire dans un enregistrement). De nouveau, l'exemple suivant imprime la 3ème 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 manière à transformer les caractères spéciaux du champ 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.
[dumb @ one pts] $ cat> fichier # Tout d'abord, nous allons 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
[dumb @ one pts] $ awk -F, fichier '{print $ 1}'
une
1
une
1
J'avais besoin d'une analyse CSV appropriée, pas de cut
/awk
et de la prière. J'essaye ceci sur un mac sans csvtool
, mais les macs 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.
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
Je pense que le plus simple est d'utiliser csvkit :
Obtient la 2ème colonne: csvcut -c 2 file.csv
Cependant, il y a aussi csvtool , et probablement un certain nombre d'autres outils de csv bash:
Sudo apt-get install csvtool
(pour les systèmes basés sur Debian)
Cela renverrait une colonne avec la première ligne ayant un "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'
Voici un exemple de fichier csv à 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.
Sortie
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 Sortie
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 8, en utilisant une virgule comme séparateur ".
cut utilise -f (signification "champs") pour spécifier les colonnes et -d (signification "délimiteur") pour spécifier le séparateur. Vous devez spécifier ce dernier point 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 ...]
En utilisant ce code depuis un certain temps, il n’est "rapide" que si vous comptez "couper et coller à partir de stackoverflow".
Il utilise les opérateurs $ {##} et $ {%%} dans une boucle au lieu de IFS. Il appelle 'err' et 'die', et ne prend en charge que les caractères virgule, tiret et pipe sous la forme 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
Je me demande pourquoi aucune des réponses à ce jour n’a mentionné csvkit.
csvkit est une suite d’outils de ligne de commande permettant de convertir et de travailler avec CSV
Je l'utilise exclusivement pour la gestion de données csv et, jusqu'à présent, je n'ai pas trouvé de problème que je n'ai pas pu résoudre avec cvskit.
Pour extraire une ou plusieurs colonnes d'un fichier cvs, vous pouvez utiliser l'utilitaire cvscut
qui fait partie de la boîte à outils. Pour extraire la deuxième colonne, utilisez cette commande:
cvscut -c 2 filename_in.csv > filename_out.csv
Vous pouvez également utiliser la boucle while
IFS=,
while read name val; do
echo "............................"
echo Name: "$name"
done<itemlst.csv