Convertir colonne en ligne et formatage de la ligne.

Résolu/Fermé
bob737 Messages postés 144 Date d'inscription jeudi 24 septembre 2009 Statut Membre Dernière intervention 14 mars 2024 - Modifié le 7 avril 2021 à 12:10
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 - 7 avril 2021 à 18:48
Bonjour,

J'ai des lignes du type

SLA1;VM1
SLA2; VM2
SLA1;VM3
SLA2; VM4
SLA1:VM5


... que j'aimerais le convertir en

SLA1; "VM1","VM3","VM5"
SLA2;"VM2","VM4"


... avec un programe bash.

Merci pour votre aide. ;-)
A voir également:

1 réponse

mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
Modifié le 7 avril 2021 à 12:09
Bonjour,

Ce que tu cherches à faire est ce qu'on appelle typiquement en base de donnée un
group by
. Dans ton cas les éléments groupés sont concaténés mais tu peux imaginer n'importe quelle opération d'agrégation en général (somme, moyenne, etc...). Pour faire un
group by
en shell tu peux passer par une commande comme
awk
.
  • Cette discussion montre comment sommer les éléments agrégés, il suffit donc de l'adapter légèrement.
  • Cette discussion montre comment les concaténer. La différence majeure est qu'on teste si la clé existe dans le tableau intermédiaire, car par défaut awk considère que les valeurs stockées sont des valeurs numériques (or ici, tu veux une chaîne de caractères).


Enfin, pour éviter d'avoir des espaces qui traîne, on remplace
$1
(la clé) et
$2
(la valeur à ajouter à la concaténation) à l'aide de gsub, comme expliqué dans cette discussion.

On obtient donc ceci :

$ awk 'BEGIN { FS = ";"; } { gsub(/^[ \t]+/, "", $1); gsub(/^[ \t]+/, "", $2); if (a[$1]) { a[$1] = a[$1] ";" $2; } else { a[$1] = $2; } } END {for (i in a) { print i ":" a[i]; } }' toto.csv
SLA1:VM1;VM3;VM5
SLA2:VM2;VM4


Voici une manière plus lisible d'obtenir le même résultat, en passant par un script awk.

toto.csv


SLA1;VM1
SLA2; VM2
SLA1;VM3
SLA2; VM4
SLA1;VM5


toto.awk

BEGIN {
    FS = ";";
}

{
    gsub(/^[ \t]+/, "", $1);
    gsub(/^[ \t]+/, "", $2);
    if (a[$1]) {
        a[$1] = a[$1] ";" $2;
    } else {
        a[$1] = $2;
    }
}

END {
    for (i in a) {
        print i ":" a[i];
    }
}


awk -f toto.awk toto.csv


Quelques explications :
  • le bloc
    BEGIN
    initialise la variable
    FS
    qui en
    awk
    , permet de préciser quel caractère sépare les colonnes (
    ;
    dans
    toto.csv
    )
  • le bloc suivant se déclenche à chaque ligne lue.
    $0
    correspond à la ligne complète
    $i
    correspond à la i-ème colonne. Avec
    gsub
    on supprime tous les espaces (voir cours sur les expressions rationnelles)
  • Ensuite, on regarde si la clé
    $1
    existe ou non dans le tableau
    a
    qui va être utilisé comme un dictionnaire clé / valeur. Cela donne l'opportunité de copier la chaîne associé à la seconde colonne (dans le
    else
    ) ou de la concaténer dans
    a[$1]
    .
  • Le bloc
    END
    est déclenché une fois le fichier complètement parcouru. On itère sur
    a
    pour générer le résultat écrit retourné par
    awk
    .


Bonne chance
2
bob737 Messages postés 144 Date d'inscription jeudi 24 septembre 2009 Statut Membre Dernière intervention 14 mars 2024
7 avril 2021 à 13:49
Merci beaucoup, ça fonctionne. j'ai juste modifié le séparateur pour que ça corresponde à mon besoin.


awk 'BEGIN { FS = ";"; } { gsub(/^[ \t]+/, "", $1); gsub(/^[ \t]+/, "", $2); if (a[$1]) { a[$1] = a[$1] "," $2; } else { a[$1] = $2; } } END {for (i in a) { print i ";" a[i]; } }' SLA_VM_capifrfe8001.intranatixis.com_106073.txt


Y a-t-il un moyen pour la sortie des VMs soit entre double quote?

soit SLA1; "VM1","VM3","VM5"
0
bob737 Messages postés 144 Date d'inscription jeudi 24 septembre 2009 Statut Membre Dernière intervention 14 mars 2024 > bob737 Messages postés 144 Date d'inscription jeudi 24 septembre 2009 Statut Membre Dernière intervention 14 mars 2024
7 avril 2021 à 14:15
en fait, pas besoin, j'ai modifié mon fichier source.


un grand merci aussi pour les explications.
0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749 > bob737 Messages postés 144 Date d'inscription jeudi 24 septembre 2009 Statut Membre Dernière intervention 14 mars 2024
7 avril 2021 à 18:48
Alors de manière générale on évite d'intervenir sur le fichier de données car il peut être lui même issu d'un programme pour lequel tu n'auras pas envie d'apporter à chaque fois des modifications manuelles. Dans ton cas si tu as compris mes explications tu as dû voir que le texte était généré dans le bloc
END
que tu peux aisément adapter comme suit :

BEGIN {
    FS = ";"
}

{
    gsub(/^[ \t]+/, "", $1);
    gsub(/^[ \t]+/, "", $2);
    if (a[$1]) {
        a[$1] = a[$1] ";" "\"" $2 "\""
    } else {
        a[$1] = "\"" $2 "\""
    }
}

END {
    for (i in a) {
        print i ":" a[i]
    }
}
0