web-dev-qa-db-fra.com

Changer le format de sortie des résultats de ligne de commande MySQL en CSV

Je souhaite obtenir des données CSV sans en-tête à partir de la sortie d'une requête vers MySQL en ligne de commande. J'exécute cette requête sur une machine différente de celle du serveur MySQL. Par conséquent, toutes les réponses de Google avec "INTO OUTFILE" ne servent à rien.

Alors je cours mysql -e "select people, places from things". Cela produit des choses qui ressemblent un peu à ceci:

+--------+-------------+
| people | places      |
+--------+-------------+
|   Bill | Raleigh, NC |
+--------+-------------+

Eh bien, ce n'est pas bon. Mais hé, regarde! Si je le redirige vers n'importe quoi, il se transforme en une liste séparée par des tabulations:

people  places
Bill    Raleigh, NC

C'est mieux - au moins, c'est analysable par programme. Mais je ne veux pas de TSV, je veux du CSV et je ne veux pas de cet en-tête. Je peux me débarrasser de l'en-tête avec mysql <stuff> | tail -n +2, mais c’est un problème que je voudrais éviter si MySQL a juste un drapeau pour l’omettre. Et je ne peux pas simplement remplacer tous les onglets par des virgules, car cela ne gère pas le contenu avec des virgules.

Alors, comment puis-je obtenir que MySQL omette l’en-tête et me donne des données au format CSV?

56
spiffytech

J'ai fini par écrire ma propre ligne de commande outil pour prendre soin de cela. Cela ressemble à cut, sauf qu'il sait quoi faire avec les champs entre guillemets, etc. Cet outil, associé à la réponse de @ Jimothy, me permet d'obtenir un CSV sans en-tête d'un serveur MySQL distant auquel je n'ai pas accès au système de fichiers. ma machine locale avec cette commande:

$ mysql -N -e "select people, places from things" | csvm -i '\t' -o ','
Bill,"Raleigh, NC"

csvmaster sur github

13
spiffytech

En réponse partielle: mysql -N -B -e "select people, places from things"

-N lui dit de ne pas imprimer les en-têtes de colonne. -B est en "mode batch" et utilise des onglets pour séparer les champs.

Si les valeurs séparées par des tabulations ne suffisent pas, voir ce Stackoverflow Q & A .

83
Jimothy

Les solutions ci-dessus ne fonctionnent que dans des cas particuliers. Vous vous exposez à toutes sortes de problèmes avec les virgules incorporées, les guillemets incorporés, ainsi que d'autres choses qui rendent le CSV difficile dans le cas général.

Faites-vous une faveur et utilisez une solution générale - faites-le correctement et vous n'aurez plus jamais à y penser. Les utilitaires de ligne de commande csvkit constituent une solution très efficace, disponibles pour tous les systèmes d'exploitation via Python. Installer via pip install csvkit. Cela vous donnera des données CSV correctes:

    mysql -e "select people, places from things" | csvcut -t

Cela produit des données séparées par des virgules avec l'en-tête toujours en place. Pour supprimer la ligne d'en-tête:

    mysql -e "select people, places from things" | csvcut -t | tail -n +2

Cela produit ce que le PO a demandé.

10
Chris Johnson

C'est comment enregistrer des résultats au format CSV côté client sans outils supplémentaires non standard. Cet exemple tilise uniquementmysql client et awk.

ne ligne:

 mysql --skip-column-names --batch -e 'select * from dump3' t | awk -F '\ t' '{sep = ""; pour (i = 1; i <= NF; i ++) {gsub (/ \\ t /, "\ t", $ i); gsub (/ \\ n /, "\ n", $ i); gsub (/ \\\\ /, "\\", $ i); gsub (/ "/,"\"\" ", $ i); printf sep"\"" $ i "\" "; sep =", "; if (i == NF) {printf"\n "} }} '

Explication logique de ce qu'il faut faire

  1. Tout d’abord, voyons comment les données ressemblent en mode RAW (avec l’option --raw). la base de données et la table sont respectivement t et dump3

    Vous pouvez voir que le champ commençant par "nouvelle ligne" (dans la première ligne) est divisé en trois lignes en raison des nouvelles lignes placées dans la valeur.

 mysql --skip-column-names --batch --raw -e 'select * from dump3' t 
 
 une ligne 2 nouvelle ligne 
 guillemets "barre oblique inverse\deux guillemets" "deux barres obliques inverses \\ deux onglets nouvelle ligne 
 la fin du champ 
 
 une autre ligne 1 une autre description de ligne sans caractère spécial 
  1. données OUTPUT en mode de traitement par lots (sans l'option --raw) - chaque enregistrement est remplacé par le texte sur une ligne par des caractères d'échappement tels que \<tab> Et new-lines
 mysql --skip-column-names --batch -e 'select * from dump3' t 
 
 une ligne 2 nouvelle ligne\nquotation "barre oblique inverse \\ deux guillemets "" deux barres obliques inverses \\\\ deux onglets\t\tnew ligne\nla fin du champ 
 une autre ligne 1 une autre description de ligne sans caractère spécial 
  1. Et sortie de données au format CSV

L'idée est de sauvegarder les données au format CSV avec des caractères d'échappement.

Pour ce faire, convertissez les entités spéciales que mysql --batch Produit (\t Sous forme d’onglets \\ Sous forme de backshlash et \n Sous forme de nouvelle ligne) en octets équivalents pour chaque élément. valeur (champ). Ensuite, la valeur entière est échappée par " Et incluse également par ". Btw - l'utilisation des mêmes caractères pour les échappements et les clôtures simplifie la sortie et le traitement, car vous n'avez pas deux caractères spéciaux. Pour cette raison, tout ce que vous avez à faire avec les valeurs (du point de vue du format CSV) est de remplacer " Par "" Dans les valeurs. De manière plus courante (avec échappement et encapsulant respectivement \ Et "), Vous devez d'abord changer \ En \\ Puis changer " dans \".

Et le explication des commandes pas à pas:

 # nous produisons une sortie d'une ligne, comme indiqué à l'étape 2. 
 mysql - noms-colonnes-skip --batch -e 'select * from dump3' t 
 
 # définit le séparateur de champs sur parce que mysql produit de cette façon 
 | awk -F '\ t' 
 
 # cela commence à itérer chaque ligne/enregistrement à partir des données mysql - comportement standard de awk 
 '{
 
 Le séparateur de champ # est vide car nous n’imprimons pas de séparateur avant le premier champ de sortie 
 sep = ""; 
 
 - itérer par chaque champ et convertir le champ en valeur appropriée csv 
 pour (i = 1; i <= NF; i ++) {
 - remarque: \\ deux shlashes au-dessous signifient\pour awk car ils sont échappés 
 
 - changer\t en octet correspondant à <tab> 
 gsub (/ \\ t/, "\ t", $ i); 
 
 - changer\n en octet correspondant à la nouvelle ligne 
 gsub (/ \\ n /, "\ n", $ i); 
 
 - transformer deux \\ en un 
 gsub (/ \\\\ /, "\\", $ i); 
 
 - changer la valeur en CSV proprement dit - changer "en" ". 
 gsub (/" /, "\"\"", $ i); 
 
 - Affiche le champ de sortie entouré de "et l'ajout d'un séparateur avant 
 printf sep"\"" $ i "\" "; 
 
 - le séparateur est défini après le traitement du premier champ - car auparavant nous n'en avions pas besoin 
 sep = ","; 
 
 - ajout d'une nouvelle ligne après le dernier champ traité - cela indique donc le séparateur d'enregistrement csv 
 si (i == NF) {printf "\ n"} 
} 
} 
9
Artur Siara

Que diriez-vous d'utiliser sed? Il est livré en standard avec la plupart (tous?) Des systèmes d'exploitation Linux.

sed 's/\t/<your_field_delimiter>/g'.

Cet exemple utilise GNU sed (Linux). Pour POSIX sed (AIX/Solaris), je pense que vous taperiez un TAB littéral au lieu de \t

Exemple (pour la sortie CSV):

#mysql mysql -B -e "select * from user" | while read; do sed 's/\t/,/g'; done

localhost,root,,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,,,,,0,0,0,0,,
localhost,bill,*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,,,,,0,0,0,0,,
127.0.0.1,root,,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,,,,,0,0,0,0,,
::1,root,,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,,,,,0,0,0,0,,
%,jim,*2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,,,,,0,0,0,0,,
2
xdaxdb

L'utilitaire mysqldump peut vous aider, principalement avec --tab option c'est un enveloppé pour SELECT INTO OUTFILE déclaration.

Exemple:

mysqldump -u root -p --tab=/tmp world Country --fields-enclosed-by='"' --fields-terminated-by="," --lines-terminated-by="\n" --no-create-info

Cela créera un fichier au format CSV /tmp/Country.txt

2
b.b3rn4rd