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

Bonjour, tout le monde :smiley:

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

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’ :wink:

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.

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 :

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 :

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

Encore, merci à toi! :smiley:

[quote=“PengouinPdt”]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 :

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. :wink:
Effectivement ta manière de faire la boucle est bien plus claire (sans compter qu’elle marche, elle :mrgreen:).

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=""[/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.

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

        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

        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. :unamused:

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 …

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 :

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

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].

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 :

il est possible d’écrire ça :

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