Utiliser sed pour nettoyer un fichier CSV

Tout d’abord, je voudrais dire que j’ai cherché de manière exhaustive cette solution. Il est important que j’utilise sed ou au moins un mélange d’utilitaires de ligne de commande * nix pour résoudre ce problème. Je m’occupe, dans certains cas, de fichiers CSV mal formés, mais je suis presque sûr que cela peut être résolu. Il me manque une seule pièce du puzzle.

Je voudrais construire un convertisseur de CSV à pipe. Cela devrait résoudre les problèmes suivants:

  1. Supprimer "," et remplacer par |
  2. Ssortingp out ", et remplacer par |
  3. Ssortingp out ," et remplacer par |
  4. Supprimez les guillemets entre guillemets comme: dog,"john "bud" smith",cat (devient dog|john bud smith|cat )
  5. Supprimer , qui ne sont pas entre guillemets et remplacer par |

J’ai terminé presque tout cela avec une commande sed , mais je suis coincé avec les virgules qui sont dans un champ. Il y a probablement une meilleure façon, mais je suis à court de pensée créative sur le sujet. Une solution appropriée parsingra cette chaîne:

 1234,"bill","butler","1000,p"r"airie",1234,6789 

dans

 1234|bill|butler|1000,prairie|1234|6789 

C’est ce que j’ai jusqu’à présent:

 echo '1234,"bill","butler","1000,p"r"airie",1234,6789' | sed -e 's/","/|/g' -e 's/,"/|/g' -e 's/",/|/g' -e 's/"//g' 

Vous pouvez utiliser perl . Text::Parsewords à la rescousse:

 perl -MText::ParseWords -nle 'print join "|", map {s/"//g; $_} parse_line(",",1,$_);' file 

Pour votre échantillon, il produirait:

 1234|bill|butler|1000,prairie|1234|6789 
 echo '1234,"bill","butler","1000,p"r"airie",1234,6789' | sed -e 's/\([0-9"]\),\([0-9"]\)/\1|\2/g' -e 's/"//g' 

J’ai défini une règle:

 , is transformed to the | if it is between numbers or quotes 

et plus tard, effacez toutes les citations

EDIT1 On dirait que ma solution ne fonctionne pas, mais il y a un bon fil pour cette question

 #!/bin/bash l='1234,"bill","butler","1000,p"r"airie",1234,6789' has_quote_in_quote() { echo $1 | grep -q '[^,]"[^,]' } clean_quote_in_quote () { echo $1 | sed -E -e 's/([^,])"([^,])/\1\2/g' } parse() { echo $1 |grep -E -o '[^"]*|"[^"]*"' } pipe_unquoted_commas() { for f in $(parse $1); do echo $f|sed -E -e '/^[^"]/s/,/|/g'; done } while has_quote_in_quote $l; do b=$(clean_quote_in_quote $l); l=$b; done echo $(printf "%s" $(pipe_unquoted_commas $b|sed 's/"//g')) 

En cours d’exécution cela donne

 1234|bill|butler|1000,prairie|1234|6789 

Ce n’est pas évident pour moi que c’est ce que vous voulez, mais laissez-moi vous expliquer comment cela fonctionne.

has_quote_in_quote trouve n’importe quel ” ‘qui n’est pas voisin d’une virgule. clean_quote_in_quote supprime tous ceux qu’il peut trouver, mais s’ils sont vraiment proches, il a besoin de plus d’un passage parce que sed a dépassé le seul caractère cité – donc que ce soit par hasard ou délibérément, votre exemple était vraiment bien choisi. Parse choisit un texte non cité ou cité, y compris les guillemets. Les guillemets sont supprimés dans la boucle while et les virgules sont transformées dans le dernier ligne, tandis que les caractères de citation restants sont supprimés.

// P