Exo en bash ^^

Exo en bash ^^ - Shell/Batch - Programmation

Marsh Posté le 20-04-2010 à 19:18:07    

Bonjour :)
 
Notre prof nous a demandé d'afficher les noms et les pourcentages des étudiants en échec en math à partir du fichier result que voici :
 
Dupond:Pierre:math:45:100
Latour:Amandine:math:42:100
Riri:Patrick:math:36:100
mathieu:Jack:geo:38:100  
 
##=>> j'ai mis exprès mathieu pour "math"(comme ça quand je vais faire la recherche dans ce fichier avec le grep je devrai faire attention à ça) et comme matière j'ai rajouté geo au lieu de math car le prof nous la demandé pour compliqué les choses ^^

 
 
                       
 
Voici comment je l'ai résolu :

 
#!bin/bash
 
echo " Quelle matière voulez-vous voir ? "
read varmatiere
 
for ligne in $(sed 's/ /:/g' result)
     do
        if [ `echo $ligne | cut -d: -f3 = $varmatiere` ]
         then
                if [ `echo $ligne | cut -d: -f4` -lt 50 ]
               then
               echo " `echo $ligne | cut -d: -f1,4` "
         
               cpt=$((cpt + 1))
               fi
        fi
done
 
Le souci c'est qu'il va chaque fois tester si chaque champs ( -f3 ) correspond bien à varmatiere et par conséquent cela met du temps.
Le prof m'a suggérer d'utiliser un grep comme ceci : search=`grep -w $varmatiere result`, seulement je ne vois pas comment l'intégrer dans mon script. Je pensais faire comme ceci :
 
#!bin/bash
 
echo " Quelle matière voulez-vous voir ? "
read varmatiere
search=`grep -w $varmatiere result`
#Mais ensuite comment l'intégrer dans ma boucle for pour qu'il l'accepte ? De plus le grep est-il vraiment la meilleur solution pour que ce soit plus efficace ?
 
for ligne in $(sed 's/ /:/g' result)
     do
        if [ `echo $ligne | cut -d: -f3 = $varmatiere` ] # le -f3 c'est le champs qui corresponds à la matière  
         then
                if [ `echo $ligne | cut -d: -f4` -lt 50 ] # le -f4 aux points
               then
               echo " `echo $ligne | cut -d: -f1,4` " # et le -f1 au nom de l'étudiant
         
               cpt=$((cpt + 1)) #Un petit compteur à la fin pour me dire le nombre d'échec en tout ^^
               fi
        fi
done
 
Voilà je remercie d'avance ceux qui ont lu mon message jusqu'au bout :p, je ne sais pas si c'est bien clair tout ce que j'ai mis, mais j'attends vos suggestions et n'hésitez à me dire si il y a des erreurs :)


Message édité par Hello87 le 21-04-2010 à 18:20:17
Reply

Marsh Posté le 20-04-2010 à 19:18:07   

Reply

Marsh Posté le 20-04-2010 à 23:57:18    

si tu peux, c'est plus rapide de passer par awk

Message cité 1 fois
Message édité par art_dupond le 20-04-2010 à 23:59:03

---------------
oui oui
Reply

Marsh Posté le 21-04-2010 à 19:17:03    

Analyse un peu ce code...
 

Code :
  1. #!/bin/bash
  2. echo " Quelle matière voulez-vous voir ? "
  3. read varmatiere
  4.  
  5. for ligne in $(grep ":$varmatiere:" result)
  6. do
  7.    echo $ligne
  8. done


 
 

art_dupond a écrit :

si tu peux, c'est plus rapide de passer par awk


Attention aux affirmations gratuites. awk est peut-être plus souple à programmer mais le programme awk étant très massif, il est assez lourd à se charger...

Message cité 1 fois
Message édité par Sve@r le 21-04-2010 à 19:17:26

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 21-04-2010 à 21:10:31    

Merci beaucoup, je l'ai introduit dans mon code et ça fonctionne, ça va légèrement plus vite que mon premier code :p. Le prof nous avait dit que sans la commande "sed" ce n'était pas possible de résoudre l'exercice preuve que non ^^
 
J'aimerai juste savoir exactement ce que veut dire =>> $( grep ":varmatiere:" result) pour être bien sûr de bien comprendre ^^
Moi je le traduit comme ça : pour chaque ligne le programme va rechercher où "math" apparaît dans le fichier result et si on fait un echo $ligne il va afficher chaque ligne qui contient ce champ "math", ai-je raison ? ^^
Et de là j'ai pu faire mes tests pour prendre que les champs qui m'intéresse et ainsi afficher que les étudiant en échec avec leurs points ^^
 

Reply

Marsh Posté le 22-04-2010 à 09:32:07    

Sve@r a écrit :


Attention aux affirmations gratuites. awk est peut-être plus souple à programmer mais le programme awk étant très massif, il est assez lourd à se charger...


C'était pas gratuit, j'ai tested avant :o

 

sur un "gros" fichier (2M entrées) :

  • awk -> moins de 10 secondes
  • le code de Hello87 -> après 1m30 ça tournait encore


Maintenant, pour l'exercice, sans doute que ça change pas grand chose. Mais ça pourrait toujours servir de savoir que awk pourrait être une alternative intéressante :)

 


edit: mmm... mais pit-être aussi que c'était le sed qui ralentissait le truc. retest ce soir :)

 

Message cité 2 fois
Message édité par art_dupond le 22-04-2010 à 10:50:52

---------------
oui oui
Reply

Marsh Posté le 22-04-2010 à 14:20:44    

art_dupond a écrit :


C'était pas gratuit, j'ai tested avant :o
 
sur un "gros" fichier (2M entrées) :

  • awk -> moins de 10 secondes
  • le code de Hello87 -> après 1m30 ça tournait encore


Maintenant, pour l'exercice, sans doute que ça change pas grand chose. Mais ça pourrait toujours servir de savoir que awk pourrait être une alternative intéressante :)
 
 
edit: mmm... mais pit-être aussi que c'était le sed qui ralentissait le truc. retest ce soir :)
 


 
J'ai pas testé sans le SED, mais je me suis demandé aussi si ce n'était pas lui qui ralentissait le truc, faudra que je test ce soir. Pour awk je ne le connais pas bien, le prof ne l'a pas mentionné, donc je vai comparer les deux codes, mon 2ème avec le grep et un avec awk ^^ Maintenant c'est sûr mon fichier result est tout petit donc ca change pas grand chose, mais le souci c'est que le prof n'en tient pas compte, il veut un truc performant :)

Message cité 1 fois
Message édité par Hello87 le 22-04-2010 à 14:25:05
Reply

Marsh Posté le 22-04-2010 à 19:29:22    

Hello87 a écrit :

Merci beaucoup, je l'ai introduit dans mon code et ça fonctionne, ça va légèrement plus vite que mon premier code :p. Le prof nous avait dit que sans la commande "sed" ce n'était pas possible de résoudre l'exercice preuve que non ^^
Il y a très souvent plusieurs façons d'arriver à un résultat....
 
 
J'aimerai juste savoir exactement ce que veut dire =>> $( grep ":varmatiere:" result) pour être bien sûr de bien comprendre ^^


grep xxx => extraction de toutes les lignes contenant le motif "xxx". Ca t'évite de te palucher la recherche à la mano. Ainsi ta boucle while ne contient que les gars avec matière "math". Toutefois, si un gars se prénomme "math", il apparaitra aussi. Désolé, j'ai pas d'idée pour demander à grep de ne chercher la matière que dans le 3° champ. J'ai bien une solution en filtrant le fichier input à base de cut mais ça implique de perdre des infos utiles par la suite. Ou alors voir la solution de art_dupond à base de awk. C'est pas forcément une mauvaise idée toutefois on sort du cadre strict du shell...
 

art_dupond a écrit :


C'était pas gratuit, j'ai tested avant :o
 
sur un "gros" fichier (2M entrées) :

  • awk -> moins de 10 secondes
  • le code de Hello87 -> après 1m30 ça tournait encore

Je comprends ton point de vue. Moi aussi j'ai eu des scripts en shell qui parsaient de gros fichiers et qui mettaient plus d'1h pour s'exécuter alors que d'autres langages plus rapides (comme awk, ou python) me traitaient ça en qq secondes. Toutefois ton affirmation initiale ne faisait pas état de la grosseur du fichier à traiter. Or c'est une caractéristique importante dans la décision de l'outil à utiliser...
 

Hello87 a écrit :

J'ai pas testé sans le SED, mais je me suis demandé aussi si ce n'était pas lui qui ralentissait le truc, faudra que je test ce soir.


Absolument pas. sed est un outils de traitement d'un flot de lignes des plus rapides qui soient.
 

Hello87 a écrit :

Maintenant c'est sûr mon fichier result est tout petit donc ca change pas grand chose, mais le souci c'est que le prof n'en tient pas compte, il veut un truc performant :)


Alors si le langage n'a pas d'importance et que tu veux un truc performant, essaye ceci
 

Code :
  1. #!/usr/bin/env python
  2. # coding: utf-8 -*-
  3.  
  4. matiere=raw_input("Quelle matière ?" )
  5.  
  6. fp=open("result", "r" )
  7. for lig in fp:
  8.     elem=lig.split(":" )
  9.     if elem[2] != matiere: continue
  10.  
  11.     print "%s, %s: matière %s %d-%d" % (elem[0], elem[1], elem[2], elem[3], elem[4])
  12. # for
  13.  
  14. fp.close()

Message cité 1 fois
Message édité par Sve@r le 22-04-2010 à 19:38:03

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 22-04-2010 à 20:43:31    

Sve@r a écrit :


Je comprends ton point de vue. Moi aussi j'ai eu des scripts en shell qui parsaient de gros fichiers et qui mettaient plus d'1h pour s'exécuter alors que d'autres langages plus rapides (comme awk, ou python) me traitaient ça en qq secondes. Toutefois ton affirmation initiale ne faisait pas état de la grosseur du fichier à traiter. Or c'est une caractéristique importante dans la décision de l'outil à utiliser...
 


Parce qu'on s'en fout de la différence dans le cas d'un pitit fichier :o  

Spoiler :

De toute façon awk gagne quand même :o


En fait c'est la lecture du fichier avec le for (ou while) qui est super super lente :sweat:  (je sens que je vais modifier quelques uns de mes scripts moi :p )


---------------
oui oui
Reply

Marsh Posté le 23-04-2010 à 23:54:21    

art_dupond a écrit :

De toute façon awk gagne quand même :o


Pas toujours. Je viens de le voir sur un test simple. Mais il gagnera si on le compare à un enchainement de pipes...
Test1: un simple grep comparé avec un awk

Citation :

prompt_$>time grep "^root:" /etc/passwd
root:x:0:0:root:/root:/bin/bash
 
real 0m0.011s
user 0m0.004s
sys 0m0.000s
 
prompt_$>time awk -F: '{if ($1 == "root" ) print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
 
real 0m0.013s
user 0m0.012s
sys 0m0.000s


 
Là, le awk est nettement plus long
 
Test2: le grep est transformé pour modifier les infos

Citation :

prompt_$>time cat /etc/passwd |grep "^root:" /etc/passwd |sed -e "s/^root:/admin:/"
admin:x:0:0:root:/root:/bin/bash
 
real 0m0.044s
user 0m0.012s
sys 0m0.028s
 
prompt_$>time cat /etc/passwd |awk -F: '{if ($1 == "root" ) {printf("admin" ); for (i=2; i<= NF; i++) printf(":%s", $i); printf("\n" )}}'
admin:x:0:0:root:/root:/bin/bash
 
real 0m0.020s
user 0m0.008s
sys 0m0.012s


Là il gagne...


Message édité par Sve@r le 23-04-2010 à 23:56:30

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 24-04-2010 à 06:27:49    

Forcément si tu changes l'énonce... :p

 

Message cité 1 fois
Message édité par art_dupond le 24-04-2010 à 06:27:56

---------------
oui oui
Reply

Marsh Posté le 24-04-2010 à 06:27:49   

Reply

Marsh Posté le 24-04-2010 à 09:22:15    

art_dupond a écrit :

Forcément si tu changes l'énonce... :p
 


 
Bah ??? J'ai rien changé. On parlait des différences de performances à utiliser le awk pour traiter du fichier. J'ai donné un exemple où je compare awk avec d'autres outils et où je traite un petit fichier. Et en plus tu devrais être content puisque mon second exemple montre que tu avais raison et que si on doit faire un gros traitement en enchainant diverses commandes pipées, vaudra alors mieux lui préférer un programme unique awk contenant le traitement complet...

Message cité 1 fois
Message édité par Sve@r le 24-04-2010 à 09:23:10

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 24-04-2010 à 17:36:49    

Sve@r a écrit :


 
Bah ??? J'ai rien changé. On parlait des différences de performances à utiliser le awk pour traiter du fichier. J'ai donné un exemple où je compare awk avec d'autres outils et où je traite un petit fichier. Et en plus tu devrais être content puisque mon second exemple montre que tu avais raison et que si on doit faire un gros traitement en enchainant diverses commandes pipées, vaudra alors mieux lui préférer un programme unique awk contenant le traitement complet...


Non mais faut pas faire attention, c'es juste que j'aime les dialogues de sourds :p
 
 
 
 
Sinon pour l'exo, c'est toi qui avais raison finalement :jap:  
 

Code :
  1. typeset -i cptr=0
  2. pattern=''
  3. while  [[ $cptr -lt 50 ]]
  4. do
  5.   pattern=$pattern'|:'${cptr}:
  6.   ((cptr+=1))
  7. done
  8. cat $fichier | egrep ^.*:.*:$matiere | egrep $pattern | cut -d ':' -f1,2,4


 
25% plus rapide que awk sur mon gros gros fichier :o
 
Enfin ça va aussi dépendre du traitement final après ça et du nombre d'échecs pour ce traitemet :)
 
 
 
En tout cas, j'aurai appris qu'il faut éviter de parcourir un fichier avec un for si c'est possible :)


Message édité par art_dupond le 25-04-2010 à 12:59:45

---------------
oui oui
Reply

Marsh Posté le 30-04-2010 à 00:21:04    

Dites, puisqu'on en est à parler de perfs entre awk et bash, je voudrais poser une pitite question.
Y-a-t-il une différence de perf entre
 

Citation :


awk -F: '{if ($1 == "root" ) print $0}' /etc/passwd  
 
et
 
awk -F: '$1~/$root^/{print $0}' /etc/passwd  
 
ou
 
awk '/$root/{print $0}' /etc/passwd  
 


Message édité par Lan Wezel le 30-04-2010 à 00:22:17
Reply

Marsh Posté le 30-04-2010 à 00:54:13    

tu fais "time ta_commande" et tu regardes ce qu'il dit :)


Message édité par art_dupond le 30-04-2010 à 00:54:47

---------------
oui oui
Reply

Marsh Posté le 30-04-2010 à 01:49:03    

oui ça bien sur, mais en fait j'ai mal posé ma question, je me demandais s'il y a une différence de comportement entre fait le test dans le { } ou avant.
 
Il y a effectivement une différence de temps d'exécution, la première etant plus lente.
Mais je me demande pourquoi.

Reply

Marsh Posté le 07-05-2010 à 17:22:04    

En fait ça ressemble un peu au principe du grep : quand tu mets un tests dans l'accolade il va faire plus d'opérations avant de se rendre compte que le tests est faux et ne pas imprimer que lorsqu'il effectue une comparaison avant les accolade.
 
De façon générale sous Unix :  
accolade = fork un fils pour accomplir ce qu'il y a dedans.
Pas d'accolade = pas de fork, donc gain de temps sur des opérations aussi simples.
 
enfin là on est dans le fonctionnement intrinsèque du AWK = je peux sans doute me planter, à valider donc avec un expert (en évitant les vérités toutes prêtes please! )


---------------
En programmation, quand t'as un problème et qu'il n'y a que deux solutions valides, seule la troisième fonctionne !
Reply

Marsh Posté le 08-05-2010 à 01:13:50    

Kerrozen a écrit :

En fait ça ressemble un peu au principe du grep : quand tu mets un tests dans l'accolade il va faire plus d'opérations avant de se rendre compte que le tests est faux et ne pas imprimer que lorsqu'il effectue une comparaison avant les accolade.


Je vois pas trop où il y a des accolades dans un grep. Toutefois, ta remarque sur le test faux qui se continue quand-même me fait me rappeler qu'en shell, si un test "and" commence par être faux, même s'il est définitivement faux, le test se poursuit quand-même jusqu'à la fin.
Exemple

Code :
  1. test "a" = "b" -a `echo salut > toto; cat toto` = "salut" && echo "ok" || echo "bad"


Le test a = b étant faux, l'ensemble complet est faux et on voit bien arriver "bad". Toutefois, le fichier "toto" est quand-même créé preuve que le test a (bien inutilement) analysé la seconde évaluation.
 
On peut aussi voir de façon analogue qu'un test "or" commençant par une évaluation vraie se poursuivra quand-même jusqu'à la fin.
 

Kerrozen a écrit :

De façon générale sous Unix :  
accolade = fork un fils pour accomplir ce qu'il y a dedans.
Pas d'accolade = pas de fork, donc gain de temps sur des opérations aussi simples.


Tu veux sans doute parler de parenthèses car les accolades en shell ont une autre signification. Cependant les parenthèses, si elles ont pour effet de permettre le regroupement de commandes, ne génèrent pas de sous-processus (ou alors il est invisible)
Exemple

Code :
  1. #!/bin/sh
  2. ps -f
  3. a=5
  4. (
  5.        ps -f
  6.        a=10
  7. )
  8. echo $a


 
L'exécution de ce script me donne un état des processus identique dans les deux cas. Cependant, la variable "a" modifiée dans les parenthèses reste inchangée une fois celles-ci refermées. Je sais pas si on peut appeler ça "sous-processus" bien que ça s'en rapproche terriblement...
 

Kerrozen a écrit :

enfin là on est dans le fonctionnement intrinsèque du AWK = je peux sans doute me planter, à valider donc avec un expert (en évitant les vérités toutes prêtes please! )


Exact. Dans le awk (qui est un programme indépendant mais ne générant qu'un seul processus), les accolades indiquent le début et fin de l'algo. Toutefois peut-être que mettre directement un code simple sans accolade le fait aller plus vite j'en sais trop rien. Mais même si c'est le cas, ce n'est pas dû à un processus en plus ou en moins. Ce sera dû uniquement à une analyse un peu plus simple donc plus rapide...

Message cité 1 fois
Message édité par Sve@r le 08-05-2010 à 01:20:18

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 10-05-2010 à 11:42:48    

Sve@r a écrit :


Tu veux sans doute parler de parenthèses car les accolades en shell ont une autre signification. Cependant les parenthèses, si elles ont pour effet de permettre le regroupement de commandes, ne génèrent pas de sous-processus (ou alors il est invisible)
Exemple

Code :
  1. #!/bin/sh
  2. ps -f
  3. a=5
  4. (
  5.        ps -f
  6.        a=10
  7. )
  8. echo $a


 
L'exécution de ce script me donne un état des processus identique dans les deux cas. Cependant, la variable "a" modifiée dans les parenthèses reste inchangée une fois celles-ci refermées. Je sais pas si on peut appeler ça "sous-processus" bien que ça s'en rapproche terriblement...
 
...
 
Exact. Dans le awk (qui est un programme indépendant mais ne générant qu'un seul processus), les accolades indiquent le début et fin de l'algo. Toutefois peut-être que mettre directement un code simple sans accolade le fait aller plus vite j'en sais trop rien. Mais même si c'est le cas, ce n'est pas dû à un processus en plus ou en moins. Ce sera dû uniquement à une analyse un peu plus simple donc plus rapide...


 
Bon alors, excusez-moi des mes propos diffus dignes d'un bafouilleur professionnel  :fou:  :o  :kaola:  
On n'est bien d'accord qu'il n'est pas question de parenthèse / accolade dans le grep qui fait très bien son travail tout seul  :sol:  
 
Pour rentrer dans le détail, l'utilisation des parenthèses implique bien la création d'un sous-shell qui est invisible pour le reste :heink:  :ouch:  (donc fork d'un processus fils par le processus en court). La portée est limitée à son propre contexte : toutes les variables utilisées ne sont pas gardées en mémoire une fois la parenthèse fermée, et on n'a pas accès aux variables du processus père à l'intérieur des parenthèses : pour revenir à l'exemple de Sve@r, le 'a' à l'intérieur de la parenthèse n'est en fait pas le même que celui du dehors.
 
Quant aux accolades, elles ne font que définir une fonction sans nom, un bloc de commandes en particulier  :jap:  : leur portée reste à l'intérieure du processus en court :
 

Code :
  1. #!/bin/sh
  2. test="toto"
  3. {
  4.   test="titi";
  5. }
  6. echo "test = $test"  # Affichera 'test = titi'


 
Pour plus d'information, voici un cours assez bien fait qui passe sur le sujet.
 
Pour revenir à la dernière question, à savoir pourquoi AWK plus rapide avec test dehors plutôt que dedans les accolades  :hello:  
 
Concernant le AWK, Sve@r nous illumine encore puisqu'effectivement le système reste celui des accolades, donc de la fonction sans nom (qu'il désigne par algo). Lorsque qu'on met un test à l'intéreur des accolades, non seulement il va analyser tout le bloc à la recherche d'une suite éventuelle au test, qu'il soit faux ou non, mais en plus il va le faire pour chaque ligne en entrée.  
Mettre le test en dehors des accolades permet de zapper tout le bloc d'un coup lorsque le test est faux et de ne traiter que les lignes concernées => gain de temps non négligeable sur une forte volumétrie. :bounce: (au passage : d'où l'intérêt de bien connaître la bête AWK pour la rendre encore plus performante ^^)
 
Voilà voilà, encore désolé pour mes petites cafouilleries  :ange:  


---------------
En programmation, quand t'as un problème et qu'il n'y a que deux solutions valides, seule la troisième fonctionne !
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed