Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 14/11/2012, à 18:47

metalux

[Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

Bonjour,
je souhaite remplir une checklist dans zenity à partir des lignes d'un fichier et que celles-ci soient cochées si ces mêmes lignes sont présentes dans un second fichier.
J’arrive à faire 2 boucles séparées qui effectuent bien les actions mais je n'arrive pas à les imbriquer.
Boucle1

choix=$(for (( x=1 ; x<=$(wc -l < fichier1) ; x++ ))
	do
		echo $ajout
		echo "$(sed -n "${x}p" fichier1)"
		echo "$(sed -n "${x}p" fichier2)"
	done | zenity --list  --checklist --separator='\n'  --print-column=3 --width=800 --height=600 --title="Choix :" --column="choix :" --column="colonne1" --column "colonne2")

$ajout doit prendre la valeur TRUE pour que la case soit cochée.
Boucle 2

grep -f fichier2 fichier3 >> .compare
while read line
do
      if grep -q $line .compare;then
               ajout="TRUE"
      else
               ajout="FALSE"
      fi
done < fichier3

Cette 2ème boucle doit, si je n'ai pas fais d'erreur, attribuer TRUE à $ajout si la ligne est présente dans les fichiers .compare et fichier3.
Comment imbriquer cette boucle dans la 1ère? A moins qu'il y ait une autre solution pour cocher dans zenity les lignes présentes dans le fichier3?

Dernière modification par metalux (Le 14/11/2012, à 23:25)

Hors ligne

#2 Le 14/11/2012, à 20:56

Postmortem

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

Salut,
Une petite remarque, je pense que tu peux remplacer :

echo "$(sed -n "${x}p" fichier1)"

Par :

sed -n "${x}p" fichier1

Sinon, suis pas sûr d'avoir compris dans quel cas tu veux que ajout soit égal à TRUE.
Alors, pour chaque ligne de fichier1, tu affiches aussi la ligne ayant le meme numéro dans fichier 2.
Et ajout doit etre égal à TRUE si la ligne de fichier2 existe dans fichier3.
C'est bien ça ??


Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »

Hors ligne

#3 Le 14/11/2012, à 21:13

metalux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

Salut Postmortem,
Juste remarque pour le sed.

Et ajout doit etre égal à TRUE si la ligne de fichier2 existe dans fichier3.
C'est bien ça ??

Je crois bien que tu as tout compris. C'est pour ça que j'ai fais une 2ème boucle avec un fichier(.compare) contenant la liste des lignes à cocher (grâce à grep -f) pour lesquelles ajout="TRUE" mais je ne sais pas si je suis sur le bon chemin. En tout cas je n'arrives pas à l'intégrer au script.

Dernière modification par metalux (Le 14/11/2012, à 21:17)

Hors ligne

#4 Le 14/11/2012, à 21:23

pingouinux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

Bonsoir,
J'étais aussi sur l'exercice, et les questions de Postmortem ont permis de clarifier la situation.
Je propose ceci :

choix=$(while read lig1 lig2
do
   grep -q "^$lig2$" fichier3 && echo true || echo false
   echo "$lig1"
   echo "$lig2"
done < <(paste fichier1 fichier2) | zenity --list  --checklist --separator='\n'  --print-column=3 --width=800 --height=600 --title="Choix :" --column="choix :" --column="colonne1" --column "colonne2")

echo choix="$choix"

Dernière modification par pingouinux (Le 14/11/2012, à 21:24)

Hors ligne

#5 Le 14/11/2012, à 21:30

tiramiseb

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

J'étais en train de réfléchir sur quelque chose d'à peu près similaire à la solution de pingouinux... Alors j'arrête de réfléchir et je donne un "+1" à pingouinux :-)

Hors ligne

#6 Le 14/11/2012, à 21:34

pingouinux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

Ce que je propose en #4 ne marche que s'il y a un seul mot par ligne.

Hors ligne

#7 Le 14/11/2012, à 22:01

metalux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

@pingouinux
J'allais justement te le signaler. Dans le fichier 1 j'ai des lignes avec plusieurs mots et seul le 1er se retrouve dans la colonne 1 et le reste dans la colonne 2, du coup ça ne passe pas.
Tu peux néanmoins m'expliquer le "^$lig2$"? Pour le reste j'ai compris, et j'ai même appris comment rediriger le résultat d'une commande directement avec < <(paste...) alors que jusqu'à maintenant je le faisais en 2 temps.
Encore une chose d'apprise mais ça ne résout pas mon problème. L'exercice reste en cours.

Hors ligne

#8 Le 14/11/2012, à 22:08

pingouinux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

Voici une version corrigée (normalement). Les explications suivent.

readarray -t FIC1 <fichier1
readarray -t FIC2 <fichier2

choix=$(for ((k=0;k<${#FIC1[@]};k++))
do
   grep -q "^${FIC2[$k]}$" fichier3 && echo true || echo false
   echo "${FIC1[$k]}"
   echo "${FIC2[$k]}"
done | zenity --list  --checklist --separator='\n'  --print-column=3 --width=800 --height=600 --title="Choix :" --column="choix :" --column="colonne1" --column "colonne2")

echo choix="$choix"

Tu peux néanmoins m'expliquer le "^$lig2$"?

^ :       Le début de ligne
$lig2 : La variable contenant une ligne de fichier2
$ :       La fin de la ligne

La concordance n'est acceptée que si les 2 lignes sont identiques, et pas si "$lig2" est un morceau d'une ligne de fichier3.

Dernière modification par pingouinux (Le 14/11/2012, à 22:15)

Hors ligne

#9 Le 14/11/2012, à 22:27

Postmortem

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

Une idée avec awk, mais j'ai pas testé, suis sur mon tel. Mais si l'idée vous semble bonne, y'a certainement quelqu'un qui pourra corriger !

choix=$(awk 'NR == FNR { ligne3[$0]=1; next } { getline ligne2 < "fichier2"; if (ligne3[ligne2]) print "TRUE"; else print "FALSE"; print $0; print ligne2 }' fichier3 fichier1) | zenity ...

Edit : rajout d'un ; avant le else. Je sais pas du c'est nécessaire... En fait, je sais meme pas si ça marche mon truc de toute manière ! tongue
Sur ce, bonne nuit à vous !

Dernière modification par Postmortem (Le 14/11/2012, à 22:34)


Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »

Hors ligne

#10 Le 14/11/2012, à 22:33

metalux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

Ta version est fonctionnelle pingouinux big_smile T'es vraiment trop fort!
Merci pour les explications, je n'avais pas pensé au "morceau d'une ligne".
J'ai passé des heures à chercher une solution et je n'aurais jamais trouvé seul. Tu peux expliquer le script fonctionnel, tout particulièrement le readarray -t que je ne connais pas et me décortiquer

choix=$(for ((k=0;k<${#FIC1[@]};k++))

si ce n'est pas trop te demander.

Hors ligne

#11 Le 14/11/2012, à 22:42

pingouinux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

@Postmortem #9 : Ça marche aussi, après avoir ajouté un ; après print "TRUE".

Hors ligne

#12 Le 14/11/2012, à 23:02

pingouinux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

@metalux #10 :

  • Voir help mapfile (synonyme de readarray, mais la doc est plus complète). Cette commande transforme un fichier en tableau (une ligne du fichier par élément du tableau).

  • for ((k=0;k<${#FIC1[@]};k++))

    C'est une boucle, k variant de 0 à ${#FIC1[@]} - 1
    ${#FIC1[@]} est le nombre d'éléments du tableau FIC1.

Voici des extraits de man bash :

${#parameter}
              Parameter  length.   The  length in characters of the value of parameter is
              substituted.  If parameter is * or @, the value substituted is  the  number
              of  positional  parameters.  If parameter is an array name subscripted by *
              or @, the value substituted is the number of elements in the array.

for (( expr1 ; expr2 ; expr3 )) ; do list ; done
              First, the arithmetic expression expr1 is evaluated according to the  rules
              described  below  under  ARITHMETIC  EVALUATION.  The arithmetic expression
              expr2 is then evaluated repeatedly until it evaluates to zero.   Each  time
              expr2  evaluates  to  a non-zero value, list is executed and the arithmetic
              expression expr3 is evaluated.

Hors ligne

#13 Le 14/11/2012, à 23:23

metalux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

@Postmortem
Aussi efficace que la solution de pingouinux. Là je suis complètement largué et je pensais que la solution était beaucoup plus simple, aussi je comprends mieux maintenant pourquoi je m'y suis cassé les dents. Pour progresser, peux-tu m'expliquer le détail de cette ligne si le coeur t'en dit et le temps te le permet... Bien que je ne connais pas awk autrement que de nom, sans doute devrais-je commencer pas là.

@tous les 2
Y a-t-il une solution préférable à l'autre? Ne vous étripez pas, ce n'est qu'une question. lol
Pour l'instant j'ai une préférence pour celle de pingouinux qui est plus dans mes capacités de compréhension.

Un grand grand MERCI pour vos solutions et vos explications, j'ai encore bien appris des choses grâce à vous .

Hors ligne

#14 Le 15/11/2012, à 10:19

Postmortem

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

Alors, la syntaxe de base de awk c'est :

awk 'pattern1 { actions }
pattern2 { actions }' fichier1 fichier2 ...

De base, awk fonctionne par ligne : une ligne = un enregistrement (on peut changer ce comportement)
Il applique à chaque enregistrement les couples pattern { actions }

Chaque enregistrement est découpé en champs ; par défaut, le séparateur est une suite de blancs (espaces ou tabulations). On peut accéder à ces champs par $1, $2 etc... L'enregistrement entier étant $0. Le dernier champ de l'enregistrement est $NF (NF étant une variable égale au nombre de champs de l'enregistrement)
Pour chaque ligne, si le pattern est vrai alors les actions correspondantes sont effectuées. Si pattern est faux, les actions correspondantes ne sont pas effectuées.

NR : variable contenant le numéro de l'enregistrement courant
FNR : variable contenant le numéro de l'enregistrement dans le fichier en cours d'analyse

awk analyse les fichiers dans l'ordre et donc, FNR est remis à 1 lorsque l'on commence à lire un nouveau fichier alors que NR continue à s'incrémenter. Par exemple, si on a passé à awk 2 fichiers de 10 lignes en paramètres, sur la dernière ligne du 2ème fichier, NR vaudra 20 alors que FNR vaudra 10.

Dans ma commande, NR == FNR est donc vrai seulement pour le 1er fichier, ici fichier3.
{ ligne3[$0]=1; next } est donc effectué pour chaque ligne de fichier3. Pour chaque ligne, on attribue la valeur 1 à un élément du tableau ligne3. Ce tableau est indicé par la chaîne de caractère qui compose la ligne. Puis on passe la commande next qui dit à awk de ne pas s'occuper des actions suivantes (ni des pattern) et de passer à l'enregistrement suivant. { getline ligne2 < "fichier2"; if (ligne3[ligne2]) print "TRUE"; else print "FALSE"; print $0; print ligne2 } n'est donc pas exécuté pour fichier3.
Si fichier3 contient ceci :

ligne 1 de fichier3
ligne 2 de fichier3

Une fois que awk aura analysé ce fichier, on aura 2 éléments dans le tableau ligne3 : ligne3["ligne 1 de fichier3"] et ligne3["ligne 2 de fichier3"]. Ces 2 éléments valent 1.

awk passe ensuite à l'analyse de fichier1.
NR == FNR est faux pour toutes les lignes de fichier1 : { ligne3[$0]=1; next } n'est donc plus exécuté.
{ getline ligne2 < "fichier2"; if (ligne3[ligne2]) print "TRUE"; else print "FALSE"; print $0; print ligne2 } est exécuté pour chaque ligne de fichier1 vu qu'il n'y a pas de pattern.

getline ligne2 < "fichier2" : chaque fois que cette commande est appelée, cela va attibuer une valeur à ligne2.
Si fichier2 contient ceci :

ligne 1 de fichier2
ligne 2 de fichier2

Au premier appel de getline, la variable ligne2 vaudra ligne 1 de fichier2 ; au 2ème appel, ligne2 vaudra ligne 2 de fichier2.
Ainsi, lorsque l'on traite la 1ère ligne de fichier1, ligne2 a pour valeur la 1ère ligne de fichier2. Lorsque l'on traite la 2ème ligne de fichier1, ligne2 a pour valeur la 2ème ligne de fichier2 et ainsi de suite.

if (ligne3[ligne2]) print "TRUE"; else print "FALSE" : cela permet de tester l'existence d'un élément du tableau ligne3. Si cet élément existe, cela veut dire qu'il y a une ligne en commun dans fichier2 et fichier3, on affiche donc TRUE. Si l'élément n'existe pas, on affiche FALSE.

print $0 : affiche la ligne en cours de fichier1
print ligne2 : affiche la valeur de ligne2

Sinon, voici la commande en un peu plus claire pour aider à la compréhension (parce que la syntaxe awk est souple) :

awk 'NR == FNR {
        ligne3[$0]=1
        next
     }
     {
        getline ligne2 < "fichier2"
        if (ligne3[ligne2]) {
           print "TRUE"
        }
        else {
           print "FALSE"
        }
        print $0
        print ligne2
     }' fichier3 fichier1

Et quasi la même en pas claire :

awk 'NR==FNR{ligne3[$0]=1;next}{getline ligne2<"fichier2";printf("%s\n%s\n%s\n",ligne3[ligne2]?"TRUE":"FALSE",$0,ligne2)}' fichier3 fichier1

La solution préférable est celle que tu comprends le mieux. Comme ça si tu veux y ajouter/modifier quelque chose, c'est plus simple.

L'avantage du awk par rapport à la solution de pingouinux, c'est que je ne lis qu'une fois fichier3 (mais en entier) alors que grep sur le fichier3 est appelé autant de fois qu'il y a de lignes dans fichier1 (mais le grep ne lit pas le fichier en entier du fait de l'option -q).

Sinon, je pense que awk serait plus rapide si les fichiers sont gros.

Et ne t'inquiète pas, même si pingouinux n'est pas d'accord avec moi, je ne l'étriperai pas !... Enfin pas tout de suite, car ses posts m'apprennent toujours plein de trucs... Par contre, une fois que j'aurai épuisé son savoir, je me gènerai pas !! gnac gnac gnac ! big_smile


Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »

Hors ligne

#15 Le 15/11/2012, à 10:49

pingouinux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

@Postmortem #14 : Et en plus, je suis d'accord avec toi…

Hors ligne

#16 Le 15/11/2012, à 13:30

metalux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

@Postmortem
Ça c'est de l'explication!
Je vais prendre le temps de lire tout ça à tête reposé. Merci pour ces explications détaillées.

La solution préférable est celle que tu comprends le mieux. Comme ça si tu veux y ajouter/modifier quelque chose, c'est plus simple.

C'est pour ça que je vais rester pour l'instant sur la solution de pingouinux, elle reprend la même structure que mon post de départ.

@pingouinux
une dernière question:
Une chose que je ne comprends pas, c'est la raison pour laquelle k=0. Comme je procédais avec sed, je partais d'une valeur de 1 et choisissais le signe <= pour l'expr2. Si je mets ces valeurs, il me manque la 1ère ligne et j'obtiens une dernière vide. Cela vient du comportement de mapfile je suppose, mais j'ai du mal à en saisir la raison, l'option -t enlevant le retour à la ligne si j'ai bien compris (et surtout si le traducteur me l'a bien traduit tongue).

En tout cas je ne regrette pas d'avoir poster, en dehors d'avoir résolu mon problème de départ, vos expériences sont très enrichissantes. Merci encore à vous 2.
@+ sur le forum

Hors ligne

#17 Le 15/11/2012, à 13:38

pingouinux

Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.

@metalux #16 :
Les indices d'un tableau de dimension N vont de 0 à N-1 (comme en C).
En ce qui concerne l'option -t, tu as bien compris.

À la prochaine.

Hors ligne