# \[Bash\] Gestion de menu: for dans select: possible ?

**URL:** <https://www.debian-fr.org/t/bash-gestion-de-menu-for-dans-select-possible/61153>\
**Category:** Programmation\
**Created:** [Février 21, 2016, 7:53 UTC](https://www.debian-fr.org/t/bash-gestion-de-menu-for-dans-select-possible/61153 "2016-02-21T19:53:43Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![PengouinPdt](https://www.debian-fr.org/user_avatar/www.debian-fr.org/pengouinpdt/32/3978_2.png) [@PengouinPdt](https://www.debian-fr.org/u/PengouinPdt)\
**Post date:** [Février 21, 2016, 7:53 UTC](https://www.debian-fr.org/t/bash-gestion-de-menu-for-dans-select-possible/61153/1 "2016-02-21T19:53:43Z")

</div>

Bonjour, tout le monde 😃

Voici un bout de code que j’ai créé ces derniers jours :

```auto
text_menu_description[1]="Rooter votre tablette"
text_menu_description[2]="Enlever le root de votre tablette"
text_menu_description[3]="Flasher une image officielle de votre tablette"
text_menu_description[4]="Flasher une Custom ROM dans votre tablette"
text_menu_description[5]="Sauvegarder votre tablette"
text_menu_description[6]="Redémarrer votre tablette"
text_menu_description[7]="Quitter le programme !"

text_menu_name[1]="ROOT"
text_menu_name[2]="UNROOT"
text_menu_name[3]="FLASH"
text_menu_name[4]="CUSTOM"
text_menu_name[5]="BACKUP"
text_menu_name[6]="REBOOT"
text_menu_name[7]="EXIT"

function display_menu() {

    if [["${ZENITY}" -ne 0]]; then

        while :
        do
            choice=$( echos_menu | zenity_list "${text_welcome}" "${title_welcome}" "${count['name']}")

            case $choice in
                "${text_menu_name[1]}") root_tablet ;;
                "${text_menu_name[2]}") unroot_tablet ;;
                "${text_menu_name[3]}") proceed_initial_rom ;;
                "${text_menu_name[4]}") proceed_custom_rom ;;
                "${text_menu_name[5]}") save_tablet ;;
                "${text_menu_name[6]}") reboot_tablet ;;
                "${text_menu_name[7]}") display_mssg "n" "${text_goodbye}" "${title_goodbye}"; exit 0 ;;
            esac
        done

    elif [["${DIALOG}" -ne 0]]; then

        while :
        do

            echos_menu
            dialog_menu "${text_welcome}" "${title_welcome}" "${count['name']}"
            rm "${FILE_TMP}.menu"

            if [[$? -eq 0]]; then
                choice=$(<"${FILE_TMP}")

                case $choice in
                    "${text_menu_name[1]}") root_tablet ;;
                    "${text_menu_name[2]}") unroot_tablet ;;
                    "${text_menu_name[3]}") proceed_initial_rom ;;
                    "${text_menu_name[4]}") proceed_custom_rom ;;
                    "${text_menu_name[5]}") save_tablet ;;
                    "${text_menu_name[6]}") reboot_tablet ;;
                    "${text_menu_name[7]}") display_mssg "n" "${text_goodbye}" "${title_goodbye}"; exit 0 ;;
                esac
            fi

        done

    else

        while :
        do
            echo "${title_welcome}"
            PS3="${text_welcome}"
            select choice in "${text_menu_description[@]}"
            do
                case $REPLY in
                    1) root_tablet ;;
                    2) unroot_tablet ;;
                    3) flash_initial_rom ;;
                    4) flash_custom_rom ;;
                    5) save_tablet ;;
                    6) reboot_tablet ;;
                    7|q|Q) display_mssg "n" "${text_goodbye}" "${title_goodbye}"; exit 0 ;;
                esac
            done
        done

    fi

    if [[$? -eq 1]]; then display_mssg "n" "${text_goodbye}" "${title_goodbye}"; fi

}

function echos_menu() {

    i=1

    if [[$DIALOG -eq 1]]; then

        while [$i -le ${#text_menu_name[@]} ]; do
            echo "${text_menu_name[$i]}|${text_menu_description[$i]}" >> "${FILE_TMP}.menu"
            let "i = $i + 1";
        done

        while IFS="|" read col1 col2 || [-n "$col1"]; do
            menu+=("$col1" "$col2")
        done < "${FILE_TMP}.menu"

    else
        while [$i -le ${#text_menu_name[@]} ]; do
            echo "${text_menu_name[$i]}";
            echo "${text_menu_description[$i]}";
            let "i = $i + 1";
        done
    fi

	unset i;

}

function zenity_list() {

    height=$(bc <<< "$3 * 45 + 5")

    zenity --column="${text_column_name}" --column="${text_column_description}" \
        --list --height="${height}" --width=500 \
        --text "$1" --title "$2"

}
```

La question que je me pose est la suivante : est-il possible d’écrire différemment ma function display\_menu pour que là où il y a les ‘case’, ce soit géré dynamiquement … par gestion d’une boucle, par exemple - quite à renommer les fonctions appelées selon la valeur contenue dans le tableau ‘text\_menu\_name’ 😉

---

<div class="post-metadata">

**Author:** ![syam](https://www.debian-fr.org/user_avatar/www.debian-fr.org/syam/32/2951_2.png) [@syam](https://www.debian-fr.org/u/syam)\
**Post date:** [Février 21, 2016, 7:53 UTC](https://www.debian-fr.org/t/bash-gestion-de-menu-for-dans-select-possible/61153/2 "2016-02-21T19:53:43Z")

</div>

Avant même de penser aux boucles, il faudrait factoriser tes 3 cas : d’abord récupérer le choix de l’utilisateur en fonction des méthodes disponibles, le normaliser éventuellement (3ème cas où on n’obtient pas un texte [mono]ROOT/UNROOT…[/mono] mais un nombre correspondant à la position dans le tableau – ne pas oublier de traiter le cas [mono]Q[/mono]), et enfin seulement traiter le choix normalisé.

Grosso modo l’algo pourrait ressembler à :

`while true; do
  CHOIX_TEXTE="" # s'assurer que le choix est invalide
  if [ZENITY]; then
    CHOIX_TEXTE="$(récupérer le choix via Zenity)"
  elif [DIALOG]; then
    CHOIX_TEXTE="$(récupérer le choix via Dialog)"
  else
    CHOIX_NUM="$(récupérer le choix via Select)"
    CHOIX_TEXTE="$(récupérer le texte correspondant à CHOIX_NUM)"
  fi
  case $CHOIX_TEXTE in
    ...
  esac
done`  
Une fois que tu auras ça, tu n’auras plus qu’à transformer ton [mono]case[/mono] en [mono]for[/mono] (ta question initiale) à l’aide d’un algo du genre :

`for INDEX in {1..${text_menu_name[#]}}; do
  if ["$CHOIX_TEXTE" = "${text_menu_name[$INDEX]}" ]; then
    "${text_menu_command[$INDEX]}" # pas besoin d'expliquer je pense, le nom de variable est clair ;)
    break
  fi
done`  
Voilà, c’est l’idée générale.

---

<div class="post-metadata">

**Author:** ![PengouinPdt](https://www.debian-fr.org/user_avatar/www.debian-fr.org/pengouinpdt/32/3978_2.png) [@PengouinPdt](https://www.debian-fr.org/u/PengouinPdt)\
**Post date:** [Février 21, 2016, 7:54 UTC](https://www.debian-fr.org/t/bash-gestion-de-menu-for-dans-select-possible/61153/3 "2016-02-21T19:54:03Z")

</div>

Je te remercie …  
Comme quoi, cela fait du bien de faire regarder par d’autres yeux … @force d’être dedans, je n’avais même plus remarqué que je pouvais circoncire mon code …

Bref, voilà la touche actuelle :

```auto
function display_menu() {

    while true; do

        if [["${ZENITY}" -ne 0]]; then
            choice=$( echos_menu | zenity_list "${text_welcome}" "${title_welcome}" "${count['name']}")

        elif [["${DIALOG}" -ne 0]]; then
            echos_menu
            dialog_menu "${text_welcome}" "${title_welcome}" "${count['name']}"

            [[$? -eq 0]] && choice=$(<"${FILE_TMP}")
            rm "${FILE_TMP}.menu"
        else
            echo "${title_welcome}"

            PS3="${text_welcome} ${text_quit} "
            select menu in "${text_menu_description[@]}"
            do
                if [["$REPLY" == "q" || "$REPLY" == "Q"]]; then
                    choice="${text_menu_name[${#text_menu_name[*]}]}"
                    break
                else
                    choice="${text_menu_name[$REPLY]}"
                    break
                fi
            done
            unset menu
        fi

        if [["$choice"]]; then
            for (( x=1 ; x <= ${#text_menu_name[@]} ; x++ )); do
                if [["$choice" == "${text_menu_name[$x]}" ]]; then
                    "${text_menu_name[$x]}"
                    break
                fi
            done
            unset x
        fi

    done

    [[$? -eq 1]] && EXIT

}
```

Comme tu le vois, la seule partie que je ne suis pas arrivé à mettre en place - si je puis m’exprimer ainsi - est ta boucle for :

```auto

```

Cela ne voulait pas fonctionner - donc en comprenant le principe, je l’ai transcris ainsi :

```auto

```

Encore, merci à toi! 😃

---

<div class="post-metadata">

**Author:** ![syam](https://www.debian-fr.org/user_avatar/www.debian-fr.org/syam/32/2951_2.png) [@syam](https://www.debian-fr.org/u/syam)\
**Post date:** [Février 21, 2016, 7:54 UTC](https://www.debian-fr.org/t/bash-gestion-de-menu-for-dans-select-possible/61153/4 "2016-02-21T19:54:04Z")

</div>

[quote=«&nbsp;PengouinPdt&nbsp;»]Comme tu le vois, la seule partie que je ne suis pas arrivé à mettre en place - si je puis m’exprimer ainsi - est ta boucle for :

```auto
for INDEX in {1..${text_menu_name[#]}}; do
```

Cela ne voulait pas fonctionner[/quote]  
Ça m’étonne pas plus que ça : j’ai du mal à mémoriser les recoins de la syntaxe shell/bash, et j’avais pas le temps d’aller fouiner dans la doc. D’où le fait que je t’aie filé des algos plutôt que du code qui marche. 😉  
Effectivement ta manière de faire la boucle est bien plus claire (sans compter qu’elle marche, elle ![:mrgreen:](https://www.debian-fr.org/uploads/default/original/1X/cc83e9b583138e48ff45f96609046b06d06c7f9d.gif "Mr. Green")).

Détail : dans le 3ème cas ([mono]select[/mono]) tu n’as pas besoin des deux [mono]break[/mono] à l’intérieur du [mono]if/else[/mono]. Un seul [mono]break[/mono] après le [mono]fi[/mono] suffit.

Et je maintiens qu’il y a besoin du [mono]choice=«&nbsp;&nbsp;»[/mono] au minimum pour le deuxième cas ([mono]dialog[/mono]) car dans cette branche il est possible que [mono]choice[/mono] ne soit jamais modifié, et donc garde la valeur qu’il avait lors de la précédente itération du [mono]while[/mono].  
Mais perso j’effacerais cette variable au début du [mono]while[/mono] plutôt qu’au début du deuxième cas, ça évitera de l’oublier si jamais un jour tu rajoutes une autre méthode de choix des options qui pose le même problème.

---

<div class="post-metadata">

**Author:** ![MisterFreez](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/m/bb73d2/32.png) [@MisterFreez](https://www.debian-fr.org/u/MisterFreez)\
**Post date:** [Février 21, 2016, 7:54 UTC](https://www.debian-fr.org/t/bash-gestion-de-menu-for-dans-select-possible/61153/5 "2016-02-21T19:54:04Z")

</div>

Il est tard, je suis probablement très fatigué, mais c’est quoi la différence entre ça :

```auto
        if [["$choice"]]; then
            for (( x=1 ; x <= ${#text_menu_name[@]} ; x++ )); do
                if [["$choice" == "${text_menu_name[$x]}" ]]; then
                    "${text_menu_name[$x]}"
                    break
                fi
            done
            unset x
        fi
```

et

```auto
        if [["$choice"]]; then
            "$choice"
        fi
```

Pour la boucle [mono]foreach[/mono] (mêm si je répugne à l’utiliser avec un break au milieu), la bonne façon de l’écrire c’est :

` for name(in "${text_menu_name[@]}" ; do
                if [["$choice" == "${name}"]]; then
                    "${name}"
                    break
                fi
            done`

J’ai pas lu tout le code, mais cette boucle me paraît louche, cette condition aussi et l’instruction au milieu aussi. 😒

---

<div class="post-metadata">

**Author:** ![PengouinPdt](https://www.debian-fr.org/user_avatar/www.debian-fr.org/pengouinpdt/32/3978_2.png) [@PengouinPdt](https://www.debian-fr.org/u/PengouinPdt)\
**Post date:** [Février 21, 2016, 7:54 UTC](https://www.debian-fr.org/t/bash-gestion-de-menu-for-dans-select-possible/61153/6 "2016-02-21T19:54:04Z")

</div>

quote="syam"  
Et je maintiens qu’il y a besoin du [mono]choice=""[/mono] au minimum pour le deuxième cas ([mono]dialog[/mono]) car dans cette branche il est possible que [mono]choice[/mono] ne soit jamais modifié, et donc garde la valeur qu’il avait lors de la précédente itération du [mono]while[/mono].  
Mais perso j’effacerais cette variable au début du [mono]while[/mono] plutôt qu’au début du deuxième cas, ça évitera de l’oublier si jamais un jour tu rajoutes une autre méthode de choix des options qui pose le même problème.[/quote]

@syam: Là, sincèrement, je ne te comprends pas.  
Dans ma condition [mono]Dialog[/mono], j’ai bien ma variable choice …

```auto

```

Et, pourquoi pas tu de son effacement ?

@MisterFreez: Ahhh, c’est la réflexion que je me suis faite hier soir …  
Pourquoi faire la dernière instruction de boucle for … alors que, justement, la variable choice détient la bonne valeur …  
Donc :

```auto

```

---

<div class="post-metadata">

**Author:** ![syam](https://www.debian-fr.org/user_avatar/www.debian-fr.org/syam/32/2951_2.png) [@syam](https://www.debian-fr.org/u/syam)\
**Post date:** [Février 21, 2016, 7:54 UTC](https://www.debian-fr.org/t/bash-gestion-de-menu-for-dans-select-possible/61153/7 "2016-02-21T19:54:04Z")

</div>

[quote=“PengouinPdt”]@syam: Là, sincèrement, je ne te comprends pas.  
Dans ma condition [mono]Dialog[/mono], j’ai bien ma variable choice …

```auto

```

C’est justement cette ligne qui pose problème.  
[mono]choice[/mono] n’est modifié que si [mono][[$? -eq 0]][/mono]. Donc si cette condition est fausse, [mono]choice[/mono] conserve sa valeur actuelle c’est à dire celle qui lui a été assignée lors de la précédente itération du [mono]while[/mono].

---

<div class="post-metadata">

**Author:** ![MisterFreez](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/m/bb73d2/32.png) [@MisterFreez](https://www.debian-fr.org/u/MisterFreez)\
**Post date:** [Février 21, 2016, 7:54 UTC](https://www.debian-fr.org/t/bash-gestion-de-menu-for-dans-select-possible/61153/8 "2016-02-21T19:54:05Z")

</div>

Je suis d’accord avec syam, même si je pense que [mono]unset choice[/mono] est plus joli qu’une chaine vide.

Au fait à la place de ça :

```auto

```

il est possible d’écrire ça :

```auto

```

que je trouve plus élégant (en plus ça fait smiley de moustachu !).
