Comment convertir des données délimitées par des tabulations en données délimitées par des virgules?

8

Je demande une liste d'instantanés ec2 via l'outil de ligne de commande ec2 d'Amazon:

ec2-describe-snapshots -H --hide-tags > snapshots.csv

Les données ressemblent à ceci:

SnapshotId      VolumeId        StartTime   OwnerId         VolumeSize  Description
snap-00b66464   vol-b99a38d0    2012-01-05  5098939         160         my backup

Comment puis-je intercepter les données avant de les rediriger snapshots.csvet effectuer les opérations suivantes:

  • remplacer "tabulations" par des virgules
  • encapsuler les valeurs avec des citations
  • si une valeur est composée de nombres, préfixez-la avec un =afin qu'Excel la traite comme du texte - par exemple, elle OwnerIddevrait être "=5098939"(celle-ci n'est pas nécessaire si elle ne peut pas être effectuée en ligne et nécessiterait plutôt un fichier de script ou une fonction)

Sortie désirée:

"SnapshotId","VolumeId","StartTime","OwnerId","VolumeSize","Description"
"snap-00b66464","vol-b99a38d0","2012-01-05","=5098939","=160","my backup"
cwd
la source
C'est là que quelqu'un vous dit d'importer à l'aide d'onglets. Ou ils le feraient, si Excel n'était pas sur le crack.
Ignacio Vazquez-Abrams
Oui, j'essaie d'aider à exceller un peu, car il ne semble pas faire si chaud tout seul. Il est toujours agréable d'avoir un fichier CSV qui peut simplement être ouvert au lieu d'avoir à utiliser la commande de menu d'importation. J'ai déjà essayé de changer l'extension en ".tsv" sans succès.
cwd
Je pense que la sortie souhaitée est un peu décalée. Vous avez beaucoup de champs vides là-dedans (les guillemets vides).
Patrick

Réponses:

10
#!/usr/bin/awk -f

BEGIN { FS = "\t"; OFS = "," }
{
    for(i = 1; i <= NF; i++) {
        if ($i + 0 == $i) { $i = "=" $i }
        else gsub(/"/, "\"\"", $i);
        $i = "\"" $i "\""
    }
    print
}

En supposant que convert.awkvous l'appelez, vous pouvez soit appeler avec

ec2-describe-snapshots -H --hide-tags | awk -f convert.awk > snapshots.csv

ou (après l'ajout des autorisations d'exécution, chmod a+x convert.awk)

ec2-describe-snapshots -H --hide-tags | ./convert.awk > snapshots.csv

Cela créera une nouvelle colonne pour chaque onglet, qui gardera la colonne de commentaire ensemble (à moins qu'elle ne contienne des onglets), mais ajoutera des colonnes vides (bien que c'est à quoi ressemble votre exemple de sortie, alors peut-être que vous le voulez vraiment). Si vous souhaitez diviser tous les espaces (cela réduira les onglets supplémentaires dans le tableau mais placera chaque mot dans la description comme une nouvelle colonne), retirez l' FS="\t";instruction.

Pour les générations futures, si vous n'avez pas besoin du "s ou du =s ou du blanc intégré, vous pouvez en faire une ligne unique:

awk -v OFS=, '{$1=$1;print}'
Kevin
la source
Belle solution propre. Je pensais que ça finirait beaucoup plus moche que ça, mais je ne suis pas une personne géniale :-)
Patrick
alors est-ce que je l'enregistre dans un fichier tel que ./convert.sh, chmod + x, puis je dirige l'entrée vers elle pour qu'elle imprime la sortie? Je reçois une erreur: /usr/bin/awk: syntax error at source line 1 context is >>> . <<< /convert.sh.
cwd
@cwd Vous pouvez l'enregistrer dans un fichier, je suggère convert.awkd'indiquer qu'il s'agit d'un awkscript et non d'un script bash. J'ai mis à jour le message avec la ligne de commande complète, et notez que j'ai ajouté un -findicateur que j'avais oublié à la première ligne (qui lui dit d'interpréter le fichier comme des commandes).
Kevin
La version à une ligne traite n'importe quel espace comme un séparateur de champ, pas seulement des tabulations. Nécessite un -F '\ t' avant le -V.
Paul_Pedant
4

Voici une solution Perl. Cela pourrait être possible avec sed / awk, mais le test de la partie numérique le rendrait probablement assez laid.

ec2-describe-snapshots -H --hide-tags | \
perl -e 'use Scalar::Util qw(looks_like_number);
         while (chomp($line = <STDIN>)) {
             print(join(",", map { "\"" . (looks_like_number($_) ? "=$_" :
                                           do {s/"/""/g; $_}) . "\"" }
             split(/\t/, $line)) . "\n");
         }' \
> snapshots.csv
Patrick
la source
3

Si vous êtes juste paresseux comme moi et que vous voulez tout faire sur une seule ligne de commande sans écrire de script, voici comment je le ferais.

ec2-describe-snapshots -H --hide-tags | sed -e 's/^I/","/g' | sed -e 's/^/"/' | sed -e 's/$/"/'> snapshots.csv

Le ^Ise fait en appuyant sur ctrl+ v i.

Le premier sedéchange tout tabspour ",". Le second sedinsère un "au début de chaque ligne et le dernier sed insère une fermeture "à la fin de chaque ligne.

Tim Kennedy
la source
Comment avez-vous réussi à faire apparaître ctrl + vi comme ça?
Burhan Khalid
@burhan La syntaxe est <kbd>text</kbd>.
jw013
3
Ou en une seule ligne: sed -e 's/^I/","/g' -e 's/.*/"&"/'ou même plus courte sed -e 's/^I/","/g;s/.*/"&"/'.
Arcege
3

Une autre solution Perl:

#!/usr/bin/perl -wln
use strict;

my($n,$s);chomp();
for $s ( split(/\t/,$_) )
{
    $s = '='.$s if ($s =~ /^\d+$/);
    $n.= '"'.$s.'",';
}
$n =~ s/(.*),/$1/;print $n;

invoquer avec ec2-describe-snapshots -H --hide-tags | /var/tmp/script.pl > output.txt

Jim
la source
Scalar :: Util n'est pas un module externe, il est livré avec Perl standard.
Patrick
Vrai. Toutes mes excuses pour la mauvaise formulation de mon commentaire. Merci pour la correction.
Jim
1

sed est l'utilitaire Linux le plus utile que j'ai jamais rencontré.

sed 's/\t/","/g' TabSeparatedValues.txt > CommaSeparatedValues.csv
sed -i 's/.*/"&"/' CommaSeparatedValues.csv

La première commande remplace tous les onglets de chaque ligne par des virgules et des guillemets. La deuxième commande insère des guillemets au début et à la fin de chaque ligne, de sorte que chaque valeur soit entourée de guillemets, ce qui permet aux virgules de faire partie de la valeur.

Paul
la source
0

Cela pourrait fonctionner pour vous:

sed 's/\t\+/,/g;s/^\|$/"/g;s/,/"&"/g;s/"\([0-9]\+\)"/"=\1"/g' file
potong
la source