Bonjour,
Je suis bloqué sur un script et je ne sais pas comment m'en sortir. Je sollicite donc votre aide.
J'ai un script qui me permet d'afficher avec une mise en page des informations provenant de plusieurs CSV.
Voici le script :
cat /var/www/cgi-bin/LPAR_MAP/*.csv | grep foo | awk -F',|;' '{print $2","$5","$6","$7}' | awk '{
print "LPARS :" $2
print "RAM : " $5
print "CPU 1 : " $6
print "CPU 2 : " $7
}'
Ce script va afficher toutes les lignes des fichiers CSV, en ne gardant que les lignes contenant le mot " foo " et n'affichant que les colonnes 2,5,6 et 7.
Voici le type de CSV que je dois traiter :
MO2PPC20;miaibv95;Running;AIX 6.1 6100-09-10-1731;60;0.6;4;DefaultPool;shared;uncap;64
MO2PPC20;miaibv161;Running;AIX 6.1 6100-09-10-1731;60;1.2;4;DefaultPool;shared;uncap;128
MO2PPC20;miaibv246;Running;AIX 6.1 6100-09-10-1731;12;0.5;1;DefaultPool;shared;uncap;64
MO2PPC20;mplaix0088;Running;AIX 7.1 7100-05-02-1810;130;1.5;11;DefaultPool;shared;uncap;64
MO2PPC20;mplaix0306;Running;AIX 7.1 7100-05-02-1810;180;0.5;3;DefaultPool;shared;uncap;64
MO2PPC20;mplaix0087;Running;AIX 7.1 7100-05-02-1810;100;0.4;4;DefaultPool;shared;uncap;64
MO2PPC20;miaibv182;Running;AIX 6.1 6100-09-10-1731;5;0.5;1;DefaultPool;shared;uncap;64
MO2PPC20;mo2vio20b;Running;VIOS 2.2.5.20;7;1.0;2;DefaultPool;shared;uncap;192
MO2PPC20;mo2vio20a;Running;VIOS 2.2.5.20;7;1.0;2;DefaultPool;shared;uncap;192
MO2PPC21;mo2vio21b;Running;VIOS 2.2.6.10;6;1.5;3;DefaultPool;shared;uncap;192
MO2PPC21;mo2vio21a;Running;VIOS 2.2.6.10;6;1.5;3;DefaultPool;shared;uncap;192
MO2PPC21;oncwasp4;Running;AIX 6.1 6100-09-10-1731;15;0.6;2;DefaultPool;shared;uncap;128
MO2PPC21;mplaix0122;Running;AIX 7.1 7100-05-02-1810;175;2.3;5;DefaultPool;shared;uncap;64
Voici ce que ça donne en sorti :
FRAME :
------------------
LPARS :mplaix0021
RAM : 11
CPU 1 : 1.0
CPU 2 : 2
LPARS :mplaix0005
RAM : 7
CPU 1 : 0.5
CPU 2 : 2
LPARS :mplaix0025
RAM : 400
CPU 1 : 36.0
CPU 2 : 46
LPARS :mplaix0019
RAM : 120
CPU 1 : 4.0
CPU 2 : 8
LPARS :mplaix0003
RAM : 26
CPU 1 : 0.5
CPU 2 : 2
LPARS :mplaix0007_OLD_DNTSTART
RAM : 0.25
CPU 1 : 0.7
CPU 2 : 7
LPARS :mplaix0009
RAM : 180
CPU 1 : 6.0
CPU 2 : 12
Le problème c'est que même si j'arrive à sélectionner seulement les lignes qui m’intéressent en faisant un grep sur certain mot( ici MO1PPC01 ), je ne sais pas à quel fichier appartiennent les informations affichées. L'idée serait d'avoir quelques choses dans ce style :
FRAME :
------------------
File : XXXXX.csv
LPARS :mplaix0021
RAM : 11
CPU 1 : 1.0
CPU 2 : 2
LPARS :mplaix0005
RAM : 7
CPU 1 : 0.5
CPU 2 : 2
File : XXXX.csv
LPARS :mplaix0025
RAM : 400
CPU 1 : 36.0
CPU 2 : 46
LPARS :mplaix0019
RAM : 120
CPU 1 : 4.0
CPU 2 : 8
LPARS :mplaix0003
RAM : 26
CPU 1 : 0.5
CPU 2 : 2
File : XXXXXX.csv
LPARS :mplaix0007_OLD_DNTSTART
RAM : 0.25
CPU 1 : 0.7
CPU 2 : 7
LPARS :mplaix0009
RAM : 180
CPU 1 : 6.0
CPU 2 : 12
Bref, que je puisse afficher le nom du fichier avec, en dessous, les informations qui lui sont propres.
Auriez-vous une solution ? Merci d'avance ! :)
# difficile ....
Posté par totof2000 . Évalué à 3.
Deja awk ne connait pas le nom du fichier source vu que tu fais un bel uuoc suivi d'un grep.
Tu pourrais remolacer ton cat | grep | awk | awk par un simple awk.
[^] # Re: difficile ....
Posté par totof2000 . Évalué à 4.
Awk dispose de la variable FILENAME qui contient le nom du fichier en cours de traitement … En plus au lieu de faire grep | awk ' { … }' tu peux faire awk '/regexp/ { …}'.
[^] # Re: difficile ....
Posté par Anonyme . Évalué à 2. Dernière modification le 16 juillet 2019 à 19:19.
je mettrais un petit : for en bash avec du awk et son option filname dedans.
je ne suis pas assez bon pour te sortir la ligne de commande directement mais un truc genre :
for fn in
ls /var/www/cgi-bin/LPAR_MAP/*
; doawk '{ print FILENAME }' tesparametre ;
done
ca devrait le faire en reflechissant un peu et t'evite de te faire pourrir avec cat :)
[^] # Re: difficile ....
Posté par totof2000 . Évalué à 4. Dernière modification le 16 juillet 2019 à 19:45.
C'est presque ça.
Il y a d'ailleurs plusieurs façons de faire.
Je vais partir de la commande de base (je suppose qu'il y a eu une erreur de copier/coller)
Soit mon fichier d'entrée toto.txt qui contient :
Si je pars de la premiere commande (corrigée, je suppose qu'il y a erreur de copier/coller a l'origine), j'ai ça :
Je veux ajouter le filename :
On voit deux problèmes: Le premier, c'est qu'à chaque ligne, le nom de fichier apparait. Le second, c'est qu'on a un '-' (correspond à stdin) parce que l'on a fait un cat | grep | awk.
Pour corriger le premier problème, il suffit de n'afficher la ligne FILENAME que lorsqu'on traite la première ligne du fichier:
Pour explication, NR correspond a l'enregistrement courant (par défaut la ligne en cours de traitement). On n'affiche le nom du fichier que si la ligne en cours de traitement est la première ligne.
Ensuite, on veut afficher le nom du fichier. Pour ça il faut virer le cat | grep | awk, tout en gardant la regexp:
Pour le traitement, j'ai ajouté un test sur une regexp et du coup on a plus besoin du grep. Il suffit maintenant d'intégrer ça dans une boucle telle que tu l'as indiqué et c'est gagné.
[^] # Re: difficile ....
Posté par totof2000 . Évalué à 4. Dernière modification le 16 juillet 2019 à 19:56.
Ah la boucle for serait plutot du genre :
[^] # Re: difficile ....
Posté par totof2000 . Évalué à 4. Dernière modification le 16 juillet 2019 à 20:25.
La deuxième méthode …
Pour info awk traite les fichiers ligne à ligne. Nous avons vu qu'on peut conditionner les traitements effectués sur des lignes en faisant des trucs du genre :
On peut aussi effectuer certaines actions avant que la première ligne du fichier soit parsée et après que la dernière ligne du fichier ait éé parsée:
Exemple, nous allons afficher un texte avant de parser le fichier, afficher chaque ligne du fichier et afficher un texte après parsing :
Dans la section BEGIN, certaines variables ne sont pas settées, FILENAME en fait partie (ici je n'affiche que dans la section begin, je n'affiche pas le contenu du fichier ni la fin) :
Par contre dans la section END:
De plus, awk permet de setter des variables en cours de parsing. On peut utiliser des tableaux:
LA section begin initialise l'index utilisé lors du parsing de chaque ligne du fichier. Cet index sera incrémenté à chaque fois qu'une ligne correspondra à la regexp. Puis à la fin, j'utilise une boucle for, qui aura comme compteur la variable j. Cette boucle va parcourir chaque tableau et afficher chaque valeur, jusqu'à ce que l'index j aura atteint la valeur de l'index i. Autrement dit c une façon de parcourir tout le tableau (mais il y en a d'autres).
[^] # Re: difficile ....
Posté par totof2000 . Évalué à 3.
Voici une autre façon de faire.
Awk peut utiliser des chaines de caractères comme indice de tableau. Au lieu d'initialiser un compteur, on peut indexer nos tableaux avec le nom de lpar.
La boucle for permet, lorsqu'on l'utilise sur un tableau, de setter la variable de boucle avec l'indice du tableau (on aurait pu le faire d'ailleurs sur le tableau a indice numérique de l'exemple précédent). On va utiliser cette propriété en bouclant sur un des tableaux qu'on a initialisé, et utiliser la variable de boucle pour afficher les éléments des autres tableaux :
[^] # Re: difficile ....
Posté par totof2000 . Évalué à 2. Dernière modification le 16 juillet 2019 à 21:02.
Attention par contre à cette méthode : lorsqu'on utilise des tableaux avec chaines de caractères comme indices, l'ordre d'arrivée des indices dans le tableau n'est pas respecté (on ne sait pas dans quel ordre les indices sont rangés). Ca se voit d'ailleurs lors de l'affichage : il ne se fait pas dans le même ordre que dans les exemples précédents.
[^] # Re: difficile ....
Posté par Makmy . Évalué à 1.
Bonjour,
Merci pour votre réponse ! J'avais bien vu la variable FILENAME dans le man mais je n'arrivais pas à la mettre en place ! Votre solution fonctionne parfaitement ! :)
[^] # Re: difficile ....
Posté par Cyril Brulebois (site web personnel) . Évalué à 7.
On peut se poser la question d'itérer avec un
for
sur la sortie dels
(en vrai : à éviter), surtout quandawk
peut gérer plusieurs fichiers d'entrée. Exemple en affichant le nom de chacun à chaque fois qu'on est sur la première ligne du fichier courant (« The input record number in the current input file ») :awk 'FNR == 1 {print FILENAME}' /var/www/cgi-bin/LPAR_MAP/*
Si on veut d'une part autoriser autant de fichiers que possible et ne pas être limité par une quelconque limite de taille maximale pour une commande, et d'autre part éviter le cas d'erreur « l'expansion du motif n'a rien donné, donc on travaille sur un fichier dont le nom est exactement
/var/www/cgi-bin/LPAR_MAP/*
», favoriserfind
est une bonne idée :find /var/www/cgi-bin/LPAR_MAP -type f | while read file; do awk 'stuff' "$file"; done
Avec un
sort
au milieu en option si l'ordre est important. Encore mieux avec-print0
etxargs -0
pour éviter les problèmes avec espaces et caractères spéciaux, mais je vais arrêter ma digression ici.Debian Consultant @ DEBAMAX
[^] # Re: difficile ....
Posté par totof2000 . Évalué à 2.
Et pourquoi pas un -exec plutôt qu'un while read file ?
[^] # Re: difficile ....
Posté par Cyril Brulebois (site web personnel) . Évalué à 1.
En vrac : ça ne permet pas d'insérer un
sort
si c'est nécessaire, gérer la syntaxe particulière ({}
et;
, ainsi que la position de la partie-exec
au sein des paramètres defind
) peut être compliqué, c'est plus difficile de garder une trace du nombre d'erreurs, etc.Et bien évidemment, cf. les avertissements concernant la sécurité de
-exec
et-execdir
dans la page de manuel defind
.Debian Consultant @ DEBAMAX
# Merci !
Posté par Makmy . Évalué à 2.
Merci à vous tous ! Vous m'avez retiré une épine du pied ! :)
[^] # Re: Merci !
Posté par totof2000 . Évalué à 2.
Merci à toi pour avoir soumis ce problème ici (ça m'a amusé de t'aider). J'espère cependant que tu ne t'es pas conténté de faire un copier/coller de la commande, mais que tu as bien compris ce que j'ai fait ;). S'il y a des zones d'ombre, n'hésite pas à demander.
[^] # Re: Merci !
Posté par Makmy . Évalué à 1. Dernière modification le 18 juillet 2019 à 09:48.
Hello ! :)
Merci encore pour tes explications ! J'ai parfaitement compris ce que tu as fait, c'est relativement simple en fait… On se demande pourquoi on n'y pense pas avant ! Là malédiction des débutants, vouloir chercher compliquer quand la solution tiens en une seule ligne ! C'est là qu'on voit ceux qui ont de l'expérience ! :)
Dernière question, dans ton exemple :
Tu te bases sur " MO2PPC ", cependant, cette variable change constamment puisqu'elle est au choix de l'utilisateur. Ce choix est contenu dans une variable, je dois donc faire quelques choses comme cela ? :
Pour qu'elle soit prise en compte ?
[^] # Re: Merci !
Posté par totof2000 . Évalué à 2. Dernière modification le 18 juillet 2019 à 13:50.
Hello.
Un truc du genre
En gros tu refermes ta quote pour que ta variable soit interprétée par le shell, et tu la réouvres ensuite pour continuer à entrer tes instructions pour awk.
Il y a d'autres méthodes mais celle-ci me parait la plus simple.
[^] # Re: Merci !
Posté par totof2000 . Évalué à 2.
Une autre façon de faire :
for fn in /var/www/cgi-bin/LPAR_MAP/*; do
awk -v var=$ma_variable -F',|;' 'NR==1 { print "FILENAME: " FILENAME }
$0 ~ $ma_variable {
…
L'option -v de awk permet de setter des variables lors de l'appel de la commande :
Ensuite l'operateur ~ permet de chercher une expression régulière dans une chaine. $0 représente l'enregistrement courant (dans notre cas, la ligne en cours de traitement).
Personnellement je préfère cette façon de faire à la première, elle évite de s'emmeler les pinceaux dans la gestion des quotes.
# simplifier la logique
Posté par NeoX . Évalué à 2.
actuellement tu dis à ton shell :
prend tous les fichiers CSV et regarde dedans (cat /dossier/.csv) => tu as une seule liste pleine de ligne, tu ne sais donc plus quel fichier est traité
dans le resultat, cherche les lignes qui contiennent **foo*,
traitement avec awk
dis lui simplement :
pour chaque fichier csv present dans /dossier/
affiche son nom
execute le awk sur le fichier
ce qui traduit en shell donnerait
d'apres les exemples données precedemments on peut evidemment faire mieux, en une seule ligne de awk, mais c'est moins lisible (pour debuter)
[^] # Re: simplifier la logique
Posté par totof2000 . Évalué à 3.
Ben non, ça donne surtout de mauvaises habitudes. C'est pas compliqué, lorsqu'on explique, de comprendre que awk est une commande qui traite un fichier ligne par ligne, et qu'on peut mettre des conditions sur les lignes traitées (les blocs BEGIN et END étant eux-même des conditions). Par contre faut juste cesser de prendre les gens qui posent les questions pour des idiots, et avoir à l'esprit qu'ils sont capables de comprendre.
[^] # Re: simplifier la logique
Posté par Cyril Brulebois (site web personnel) . Évalué à 3.
Quitte à être tatillon :
awk
travaille sur des enregistrements plutôt que sur des lignes. Ce raccourci est compréhensible vu la valeur par défaut deRS
, mais autant être précis ?;)
Debian Consultant @ DEBAMAX
[^] # Re: simplifier la logique
Posté par totof2000 . Évalué à 2.
Tu as parfaitement raison.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.