web-dev-qa-db-fra.com

Comment faire pour que awk ignore le délimiteur de champ entre guillemets doubles?

J'ai besoin de supprimer 2 colonnes dans un fichier de valeurs séparées par des virgules. Considérez la ligne suivante dans le fichier csv:

"[email protected],www.example.com",field2,field3,field4
"[email protected]",field2,field3,field4

Maintenant, le résultat que je veux à la fin:

"[email protected],www.example.com",field4
"[email protected]",field4

J'ai utilisé la commande suivante:

awk 'BEGIN{FS=OFS=","}{print $1,$4}'

Mais la virgule intégrée qui se trouve entre guillemets crée un problème, voici le résultat que j'obtiens:

"[email protected],field3
"[email protected]",field4

Maintenant, ma question est de savoir comment faire pour que awk ignore les "," qui sont à l'intérieur des guillemets doubles?

24
Deepak K M

Dans le manuel GNU awk ( http://www.gnu.org/software/gawk/manual/gawk.html#Splitting-By-Content ):

$ awk -vFPAT='([^,]*)|("[^"]+")' -vOFS=, '{print $1,$4}' file
"[email protected],www.example.com",field4
"[email protected]",field4

et voyez Quel est le moyen le plus robuste pour analyser efficacement CSV en utilisant awk? pour analyser plus généralement les CSV qui incluent des sauts de ligne, etc. dans les champs.

36
Ed Morton

Ce n'est pas une solution bash/awk, mais je recommande CSVKit , qui peut être installé par pip install csvkit. Il fournit une collection d'outils de ligne de commande pour travailler spécifiquement avec CSV, y compris csvcut , qui fait exactement ce que vous demandez:

csvcut --columns=1,4 <<EOF
"[email protected],www.example.com",field2,field3,field4
"[email protected]",field2,field3,field4
EOF

Sortie:

"[email protected],www.example.com",field4
[email protected],field4

Il supprime les citations inutiles, ce qui, je suppose, ne devrait pas être un problème.

Lisez les documents de CSVKit ici sur RTD . ThoughtBot a un Joli petit billet de blog présentant cet outil, c'est là que j'ai découvert CSVKit.

10
4ae1e1

Dans votre exemple de fichier d'entrée, c'est le premier champ et uniquement le premier champ, qui est cité. Si cela est vrai en général, considérez ce qui suit comme une méthode pour supprimer les deuxième et troisième colonnes:

$ awk -F, '{for (i=1;i<=NF;i++){printf "%s%s",(i>1)?",":"",$i; if ($i ~ /"$/)i=i+2};print""}' file
"[email protected],www.example.com",field4
"[email protected]",field4

Comme mentionné dans les commentaires, awk ne comprend pas nativement les séparateurs entre guillemets. Cette solution fonctionne autour de cela en recherchant le premier champ qui se termine par un devis. Il ignore ensuite les deux champs suivants.

Les détails

  • for (i=1;i<=NF;i++)

    Cela démarre un for sur chaque champ i.

  • printf "%s%s",(i>1)?",":"",$i

    Ceci imprime le champ i. S'il ne s'agit pas du premier champ, le champ est précédé d'une virgule.

  • if ($i ~ /"$/)i=i+2

    Si le champ actuel se termine par un guillemet double, cela incrémente alors le compteur de champs de 2. C'est ainsi que nous sautons les champs 2 et 3.

  • print""

    Une fois que nous avons terminé avec la boucle for, cela affiche une nouvelle ligne.

4
John1024

Cet awk devrait fonctionner indépendamment de l'emplacement du champ entre guillemets et fonctionne également avec les guillemets échappés.

awk '{while(match($0,/"[^"]+",|([^,]+(,|$))/,a)){
      $0=substr($0,RSTART+RLENGTH);b[++x]=a[0]}
      print b[1] b[4];x=0}' file

Contribution

"[email protected],www.example.com",field2,field3,field4  
"[email protected]",field2,field3,field4  
field1,"[email protected],www.example.com",field3,field4  

Sortie

"[email protected],www.example.com",field4
"[email protected]",field4
field1,field4

Cela fonctionne même sur

field1,"field,2","but this field has ""escaped"\" quotes",field4

Que la puissante variable FPAT échoue!


Explication

 while(match($0,/"[^"]+",|([^,]+(,|$))/,a))

Démarre une boucle while qui continue tant que la correspondance est réussie (c'est-à-dire qu'il y a un champ).
La correspondance correspond à la première occurrence de l'expression régulière qui correspond incidemment aux champs et la stocke dans le tableau a

 $0=substr($0,RSTART+RLENGTH);b[++x]=a[0]

Ensembles $0 pour commencer à la fin du champ correspondant et ajoute le champ correspondant à la position de tableau correspondante dans b.

  print b[1] b[4];x=0}

Imprime les champs souhaités à partir de b et remet x à zéro pour la ligne suivante.


Des défauts

Échouera si le champ contient à la fois des guillemets d'échappement et une virgule


Modifier

Mis à jour pour prendre en charge les champs vides

awk '{while(match($0,/("[^"]+",|[^,]*,|([^,]+$))/,a)){
     $0=substr($0,RSTART+RLENGTH);b[++x]=a[0]}
     print b[1] b[4];x=0}' file
1
user4453924