#1 Le 14/11/2012, à 19: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 15/11/2012, à 00:25)
Hors ligne
#2 Le 14/11/2012, à 21: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, à 22: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, à 22:17)
Hors ligne
#4 Le 14/11/2012, à 22: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, à 22:24)
Hors ligne
#5 Le 14/11/2012, à 22: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 :-)
Sébastien Maccagnoni - https://www.maccagnoni.eu - https://www.domotego.com
Hors ligne
#6 Le 14/11/2012, à 22: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, à 23: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, à 23: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, à 23:15)
Hors ligne
#9 Le 14/11/2012, à 23: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 !
Sur ce, bonne nuit à vous !
Dernière modification par Postmortem (Le 14/11/2012, à 23:34)
Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »
Hors ligne
#10 Le 14/11/2012, à 23:33
- metalux
Re : [Résolu]aide pour l'imbrication de 2 boucles dans un script bash.
Ta version est fonctionnelle pingouinux 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, à 23: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 15/11/2012, à 00: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 15/11/2012, à 00: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.
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, à 11: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 !
Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »
Hors ligne
#15 Le 15/11/2012, à 11: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, à 14: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 ).
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, à 14: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