J'essaie d'analyser un CSV contenant potentiellement 100k + lignes. Voici les critères que j'ai:
Je voudrais récupérer toutes les lignes du CSV qui ont la valeur donnée dans l'index donné (délimité par des virgules).
Des idées, en tenant particulièrement compte de la performance?
Premier prototype utilisant les anciens grep
et cut
:
grep ${VALUE} inputfile.csv | cut -d, -f${INDEX}
Si c'est assez rapide et donne la sortie appropriée, vous avez terminé. :)
Comme alternative aux monolignes basées sur cut
ou awk
, vous pouvez utiliser le csvtool
aka ocaml-csv
:
$ cat yourfile | csvtool -t ',' col "$index" - | grep "$value"
Selon les documents, il gère les échappements, les citations, etc.
Voir cette vidéo youtube: Leçon 10 de script BASH avec des fichiers CSV
Fichier CSV:
Bob Brown;Manager;16581;Main
Sally Seaforth;Director;4678;HOME
Script bash:
#!/bin/bash
OLDIFS=$IFS
IFS=";"
while read user job uid location
do
echo -e "$user \
======================\n\
Role :\t $job\n\
ID :\t $uid\n\
SITE :\t $location\n"
done < $1
IFS=$OLDIFS
Sortie:
Bob Brown ======================
Role : Manager
ID : 16581
SITE : Main
Sally Seaforth ======================
Role : Director
ID : 4678
SITE : HOME
CSV n'est pas aussi simple que cela. Selon les limites des données dont vous disposez, vous devrez peut-être vous soucier des valeurs entre guillemets (qui peuvent contenir des virgules et des retours à la ligne) et des guillemets d'échappement.
Donc, si vos données sont suffisamment restreintes, vous pouvez vous contenter d'une simple séparation par virgule, le script Shell peut le faire facilement. Si, en revanche, vous devez analyser CSV "correctement", bash ne serait pas mon premier choix. Au lieu de cela, je regarderais un langage de script de niveau supérieur, par exemple Python avec un csv.reader .
Dans un fichier CSV, chaque champ est séparé par une virgule. Le problème est qu'un champ lui-même peut avoir une virgule intégrée:
Name,Phone
"Woo, John",425-555-1212
Vous avez vraiment besoin d'un package de bibliothèque qui offre une prise en charge CSV robuste au lieu de compter sur l'utilisation d'une virgule comme séparateur de champ. Je sais que les langages de script tels que Python a un tel support. Cependant, je suis à l'aise avec le langage de script Tcl donc c'est ce que j'utilise. Voici un script Tcl simple qui fait ce que vous demandez pour:
#!/usr/bin/env tclsh
package require csv
package require Tclx
# Parse the command line parameters
lassign $argv fileName columnNumber expectedValue
# Subtract 1 from columnNumber because Tcl's list index starts with a
# zero instead of a one
incr columnNumber -1
for_file line $fileName {
set columns [csv::split $line]
set columnValue [lindex $columns $columnNumber]
if {$columnValue == $expectedValue} {
puts $line
}
}
Enregistrez ce script dans un fichier appelé csv.tcl et appelez-le en tant que:
$ tclsh csv.tcl filename indexNumber expectedValue
Le script lit le fichier CSV ligne par ligne et stocke la ligne dans la variable $ ligne, puis il divise chaque ligne en une liste de colonnes (colonnes $ variables). Ensuite, il sélectionne la colonne spécifiée et l'affecte à la variable $ columnValue. En cas de correspondance, imprimez la ligne d'origine.
Utilisation de awk
:
export INDEX=2
export VALUE=bar
awk -F, '$'$INDEX' ~ /^'$VALUE'$/ {print}' inputfile.csv
Edit: Selon Dennis Williamson's excellent commentaire, cela pourrait être écrit de façon beaucoup plus nette (et sûre) en définissant des variables awk en utilisant le -v
commutateur:
awk -F, -v index=$INDEX -v value=$VALUE '$index == value {print}' inputfile.csv
Bon sang ... avec des variables, et tout, awk est presque n vrai langage de programmation ...
Pour les situations où les données ne contiennent aucun caractère spécial, la solution suggérée par Nate Kohl et ghostdog74 est bonne.
Si les données contiennent des virgules ou des sauts de ligne à l'intérieur des champs, awk peut ne pas compter correctement les numéros de champ et vous obtiendrez des résultats incorrects.
Vous pouvez toujours utiliser awk, avec l'aide d'un programme que j'ai écrit appelé csvquote (disponible sur https://github.com/dbro/csvquote ):
csvquote inputfile.csv | awk -F, -v index=$INDEX -v value=$VALUE '$index == value {print}' | csvquote -u
Ce programme trouve des caractères spéciaux dans les champs entre guillemets et les remplace temporairement par des caractères non imprimables qui ne confondent pas awk. Ensuite, ils sont restaurés une fois awk terminé.
index=1
value=2
awk -F"," -v i=$index -v v=$value '$(i)==v' file
Une solution sed
ou awk
serait probablement plus courte, mais en voici une pour Perl:
Perl -F/,/ -ane 'print if $F[<INDEX>] eq "<VALUE>"`
où <INDEX>
est basé sur 0 (0 pour la première colonne, 1 pour la deuxième colonne, etc.)
Je cherchais une solution élégante qui prend en charge les devis et ne nécessiterait rien d'installation de fantaisie sur mon appliance VMware vMA. Il s'avère que ce simple script python fait l'affaire! (J'ai nommé le script csv2tsv.py
, car il convertit CSV en valeurs séparées par des tabulations - TSV)
#!/usr/bin/env python
import sys, csv
with sys.stdin as f:
reader = csv.reader(f)
for row in reader:
for col in row:
print col+'\t',
print
Les valeurs séparées par des tabulations peuvent être facilement divisées avec la commande cut (aucun délimiteur ne doit être spécifié, tab est la valeur par défaut). Voici un exemple d'utilisation/sortie:
> esxcli -h $VI_Host --formatter=csv network vswitch standard list |csv2tsv.py|cut -f12
Uplinks
vmnic4,vmnic0,
vmnic5,vmnic1,
vmnic6,vmnic2,
Dans mes scripts, je vais en fait analyser la sortie tsv ligne par ligne et utiliser read ou cut pour obtenir les champs dont j'ai besoin.