Comment sortir d'un script bash avec menu et sous-menu

Résolu

Bonsoir,

J'ai créé un script test simplifié ( il y a normalement 5 options) pour lancer des programmes avec un menu et sous-menu. L'installation se déroule normalement. Par contre, je ne peux pas sortir du programme et la fonction suivante ne peut pas d'exécuter.

Voici le script :

#!/bin/bash
# Essai de menu et sous-menu


# Sélection de l'archive oStorybook
PS3='Faites votre choix: '
options=("oStorybook" "Exit")
select opt in "${options[@]}"
do
    if [ "$opt" == "Exit" ] ; then
       echo "Au revoir !"
       break
    fi

    case $opt in
        "oStorybook")
         PS3='Quel choix ?'
    options=("jar" "sources"  "Quit")   
    select model in "${options[@]}"
    do         
         case $model in
            "jar")
                echo "l'archive jar va s'installer"
                wget $quiet1 -nv    http://download.tuxfamily.org/ostorybook/next/oStorybook.jar
                 break ;;                                                                  
            "sources")           
                echo "Installation des sources"
                wget $quiet1 -nv http://download.tuxfamily.org/ostorybook/next/oStorybook_src-5.05.08.zip     
                break;;
            "Quit")  
             exit
            ;;   
            #*) echo "invalid option $REPLY";;                      
        esac
    done             
       
    esac
done  

# Installation de nerd-dictation (pour poursuivre l'essai)
    echo ">> Installation de nerd-dictation"
    git clone https://github.com/ideasman42/nerd-dictation.git
   

Je reviens systématiquement sur :

Quel choix ?

Qui aurait une solution ? Merci à tous.

12 réponses

  1. Merci pour ces pistes. Le packaging est à l'état de projet car il reste d'autres points à développer.

    1. Il ne s'agit nullement de ré-développer un programme existant mais de l'adapter, ce qui est bien différent. Le projet nerd-dictation est disponible sur Github. Combien d'utilisateurs de l'outil informatique connaissent ce site et combien seraient capables de récupérer l'archive, de l'installer, d'installer les dépendances et de configurer le fichier linguistique ? C'est ce que nous faisons actuellement pour proposer une fonctionnalité opérationnelle ne nécessitant aucune manipulation de la part des utilisateurs.

      C'est également ce qu'a fait Papoteur qui a écrit un GUI pour nerd-dictation, Elograf qui malheureusement ne fonctionne pas sur toutes les distros ( Ubuntu Mate en fait partie).

      1. Modérateur

        Ah très bien, je ne connaissais pas mais effectivement si c'est l'adapter, c'est parfait !

        Quelques pistes pour le packaging : le projet nerd-dictation dispose d'un setup.py, ce qui signifie que le paquet est plus ou moins prêt à être packagé sur Pypi (tous les OS), en rpm (distributions basées sur redhat) ou en deb (distributions basées sur debian). Si ton projet dépend de nerd-dictation et d'autres projets, tu peux tout à fait faire ton propre paquet avec ses propres dépendances. Pour plus de détails, voir ce tutoriel.

    2. Merci. je comprends bien la structure du script. Par contre, je n'ai toujours pas compris comment ajouter une commande pour poursuivre le script une fois le choix effectué.

      Pour illustrer mon propos, voici ce qui a motivé mes messages :

      Je contribue au développement (porteur d'idées, testeur et accessoirement "codeur !)) de oStorybook.

      Ayant découvert un programme de Speech to text (nerd-dictation), j'ai proposé d'intégrer ce logiciel dans oStorybook, non pas pour écrire des livres "à la volée" mais pour faciliter la prise de notes, les remarques, etc. Une précision. Une fois lancé, ce programme tourne en tâche de fond et envoie les mots reconnus dans l'application qui est active (courrier, traitement de texte, console, etc)

      A l'occasion, je me suis attelé avec le développeur principal à la réalisation du script pour automatiser l'installation.

      Le script d'installation lit et exécute les différentes commandes : définition des répertoires d'installation, dépendances, etc. Viennent ensuite la choix de la langue et du modèle linguistique "léger" ou "complet". C'est cette phase qui me posait problème et que j'ai traitée d'une manière détournée (cf #8) avec car, une fois les choix effectués, le programme ne se poursuivait pas. D'où la simulation avec la commande "date" dans mes messages.

      Concrètement, j'ai ajouté la commande "whoami" à la fin de ton dernier script, après Exit. La sortie ne m'a pas étonné, la commande reste ignorée. C'est cette difficulté  sûrement très triviale que je voulais résoudre. Et comme je suis curieux de nature...

      J'ai finalement trouvé un moyen détourné pour réaliser le script qui fonctionne.

      Encore merci pour ta disponibilité.

      1. Modérateur

        Par contre, je n'ai toujours pas compris comment ajouter une commande pour poursuivre le script une fois le choix effectué.

        Peux-tu clarifier ? Si je reprends le message #10, si tu choisis Exit, comme maintenant j'ai mis un break (au lieu de exit), cela te fait poursuivre le programme ligne 51 (au lieu de quitter le script).

        Ayant découvert un programme de Speech to text (nerd-dictation), j'ai proposé d'intégrer ce logiciel dans oStorybook, non pas pour écrire des livres "à la volée" mais pour faciliter la prise de notes, les remarques, etc. Une précision. Une fois lancé, ce programme tourne en tâche de fond et envoie les mots reconnus dans l'application qui est active (courrier, traitement de texte, console, etc)

        Pourquoi pas, mais de tels outils existent déjà. Par exemple, dans les hôpitaux, certains docteurs ont une espèce de micro qui leur permettent de remplir les dossiers. Ils dictent également les ponctuations et les retours à la lignes, et font quelques corrections mineures au clavier. Cette page recense quelques outils analogues sous Linux. Peut-être qu'il vaut mieux consacrer son énergie à contribuer à un projet existant (pour l'améliorer) plutôt que re-développer quelque chose qui a déjà été fait, à bon entendeur ;-)

        Afin de consacrer un fil de discussion par sujet, si tu veux plus de détails, merci d'ouvrir un nouveau fil de discussion.

        Concrètement, j'ai ajouté la commande "whoami" à la fin de ton dernier script, après Exit. La sortie ne m'a pas étonné, la commande reste ignorée. C'est cette difficulté  sûrement très triviale que je voulais résoudre. Et comme je suis curieux de nature...

        Si tu as bien recopié le script #10 et ajouté ligne 51 la commande whoami, normalement ça fait ce que tu veux.

        1) A
        2) B
        3) Exit
        > 3
        Exit
        mando
        

        Si j'ai mal compris ce que tu veux faire, merci de clarifier.

        Bonne chance

    3. Modérateur

      Bonjour,

      Concernant ton message #8, je n'ai pas compris : la commande date s'exécute quand tu rencontres la commande (au même titre que les echo que j'ai mis en exemple), mais j'imagine que tu as depuis contourné le problème. En tout cas l'idée est toujours la même que dans mon message #7 : la manière dont sont imbriqués les sous-menus est reflétée par la manière dont sont imbriqués les select.

      Voilà à quoi ça pourrait ressembler :

      #!/bin/bash
      # Define the menu list here
      PS3="> "
      select opt in "A" "B" "Exit"
      do
          case "$opt" in
          "Exit")
              echo "Exit"
              break
              ;;
          "A")
              PS3=">> "
              select subopt in "Quit" "A1" "A2"
              do
                  case "$subopt" in
                      "Quit")
                          echo "Quit"
                          break
                          ;;
                      "A1")
                          echo "A1"
                          ;;
                      "A2")
                          echo "A2"
                          ;;
                  esac
              done
              ;;
          "B")
              PS3=">> "
              select subopt in "Quit" "B1" "B2"
              do
                  case "$subopt" in
                      "Quit")
                          echo "Quit"
                          break
                          ;;
                      "B1")
                          echo "B1"
                          ;;
                      "B2")
                          echo "B2"
                          ;;
                  esac
              done
              ;;
      
          esac
          PS3="> "
      done
      

      Exécution :

      (mando@silk) (~) $ bash toto.sh 
      1) A
      2) B
      3) Exit
      > 1
      1) Quit
      2) A1
      3) A2
      A> 3
      A2
      A> 2
      A1
      A> 1
      Quit
      > 2
      1) Quit
      2) B1
      3) B2
      B> 2
      B1
      B> 3
      B2
      B> 1
      Quit
      > 3
      Exit

      Concernant ton message #9, c'est une manière plus basique de faire. Contrairement à celle que je viens de proposer; elle n'utilise pas select, donc elle s'applique à des shells autre que bash.

      Bonne chance

      1. Bonjour,

        J'ai résolu la question en adaptant un script trouvé sur le Net proposant menu et sous-menu.  Voici une version simplifiée. Si elle peut être utile à certains néophytes comme moi, tant mieux !

        Encore merci.

        #!/bin/bash
        # Script avec menu et sous-menu
        while :
        do
        echo "
        ----- SELECT  -----
        
        (1)  A
        (2)  B
        (3)  Q = Quitter
        "
        
        read choix_
        case $choix  in
         1) echo " -----sous-menu 1-----
        
            1)  A1
            2)  A2
            3)  Q = Quitter
            "
            read choix sous-menu 1
            echo
            case $choix sous-menu 1 in
            1) echo "vous avez sélectionné A1 "
            commande A1 ;;
            2) echo "vous avez sélectionné A2 "
            commande A2;;
            3| Q | q ) echo ; exit 0 ;;
            *) echo "Erreur saisie sous-menu 1";;
            esac
            ;;
        
         2) echo " -----sous-menu 2-----
        
            1)  B1
            2)  B2
            3)  Q = Quitter
            "
            read choix sous-menu 2
            echo
            case $choix sous-menu 2 in
            1) echo "vous avez sélectionné B1 "
            commande B1;;
            2) echo "vous avez sélectionné B2 "
            commande B2;;
            3| Q | q ) echo ; exit 0 ;;
            *) echo "Erreur saisie sous-menu 2";;
            esac
            ;;
        
        3) | Q | q ) echo ; exit 0
        ;;
        
        *) echo "Erreur saisi menu principal"
        ;;
        	esac
        done
        1. Merci pour ta démonstration très claire. La difficulté que je rencontre consiste à poursuivre l'exécution des commandes. Si tu regardes bien, je précisais dans mon premier message qu'après le choix effectué, l'exécution devait se poursuivre avec autre commande :

          # afficher la date (autre commande pour vérifier la poursuite de l'installation) 
          	 
          	date +"Aujourd'hui, nous sommes le %A %d %B. Il est %T." 

          C'est à ce niveau que le programme bloquait. Cette commande n'était lue qu'en cas de sortie de la partie du script traitant du choix. 

          1) Commands
          2) Exit
          > 2
          Exit
          Aujourd'hui, nous sommes le vendredi 28 octobre. Il est 12:55:44.
          
          
          ------------------
          (program exited with code: 0)
          Press return to continue
          

          J'ignore s'il est possible de traiter cette commande dans le script.

          1. Modérateur

            Bonjour,

            J'ai trouvé le temps de regarder comment écrire ton script en bash, voici à quoi ça peut ressembler :

            #!/bin/bash
            # Define the menu list here
            PS3="> "
            select opt in "Commands" "Exit"
            do
                if [ "$opt" == "Exit" ] ; then
                    echo "Exit"
                    break
                fi
            
                PS3=">> "
                select subopt in "pwd" "curl" "Quit"
                do
                    case "$subopt" in
                        "Quit")
                            echo "Quit"
                            break
                            ;;
                        "pwd")
                            echo "pwd"
                            ;;
                        "curl")
                            echo "curl"
                            ;;
                    esac
                done
            
                PS3="> "
            done

            Pour s'y retrouver, tu noteras que je change à la volée l'apparence de l'invite de commande :

            • > pour le menu principal
            • >> pour le sous menu

            Concernant le premier break : celui qui interrompt la boucle select opt. On arrive donc à la dernière ligne du programme et donc on quitte le script. Comme rien n'est fait après le menu, on aurait aussi pu écrire exit 0, ce qui quitte immédiatement le programme.

            Concernant le second break : celui qui interrompt la boucle select subopt. Si on voulait quitter le programme avec cette option, il faudrait le remplacer par exit 0.

            Exemple :

            1) Commands
            2) Exit
            > 1
            1) pwd
            2) curl
            3) Quit
            >> 1
            pwd
            >> 1
            pwd
            >> 2
            curl
            >> 3
            Quit
            > 2
            Exit

            Bonne chance

            1. Merci pour ces précisions. Je n'ai pas encore eu le temps de regarder attentivement !

              1. Bonjour,

                Merci beaucoup d'avoir consacré une partie de ton temps à répondre à ma question. Je vais étudier le script en détail et l'adapter au mieux.

                Bonne journée.

                Cordialement.

                1. Modérateur

                  Parfait, en fait je pense que dans ton cas, ligne 30, si tu remplaces exit par break ça devrait suffire (exit quitte le programme au lieu de quitter la boucle la plus enveloppante). Note par ailleurs que si tu veux suivre exactement le code que je t'ai donné, bash permet de déclarer des fonctions.

                  Tu peux aussi adapter le script python que je t'ai donné à tes besoins, car c'est un langage bien plus confortable que le bash. Le bash prend surtout son sens quand on veut lancer des commandes plus compliquées.

              2. Modérateur

                Bonjour,

                Pardon pour ma réponse tardive je manque de temps en ce moment. En fait il y a un problème au niveau de la structure de ton algorithme, je t'écris le structure du code en python et te laisse l'adapter en bash.

                Code :

                #!/usr/bin/env python3
                
                import sys
                
                def select(choices):
                    s = None
                    while s not in choices:
                        for choice in choices:
                            print(choice)
                        s = input("type your choice: ")
                    return s
                
                def menu():
                    while True:
                        choice1 = select({"exit", "commande"})
                        if choice1 == "exit":
                            sys.exit(0)
                        else:
                            while True:
                                choice2 = select({"pwd", "curl", "quit"})
                                if choice2 == "pwd":
                                    print("--> pwd")
                                elif choice2 == "curl":
                                    print("--> curl")
                                elif choice2 == "quit":
                                    break
                
                menu()

                break permet d'interrompre la boucle la plus enveloppante. Ici le premier break interrompt donc la première boucle while, et le deuxième break interrompt la 2e boucle while. Ainsi :

                • quand on choisit "exit" (première boucle) on interrompt la première boucle while et donc le programme ;
                • quand on fait "quit" (deuxième boucle), on interrompt la deuxième boucle et donc on refait un tour dans la première boucle while (ce qui revient à afficher le premier menu).

                J'ai de plus défini une fonction select afin que le programme redemande à l'utilisateur de faire son choix si celui-ci n'est pas conforme à l'ensemble des valeurs valides.

                Exécution :

                (mando@silk) (~) $ python3 toto.py 
                commande
                exit
                type your choice: commande
                quit
                pwd
                curl
                type your choice: pwd
                --> pwd
                quit
                pwd
                curl
                type your choice: plop
                quit
                pwd
                curl
                type your choice: curl
                --> curl
                quit
                pwd
                curl
                type your choice: quit
                commande
                exit
                type your choice: exit
                

                Bonne chance

                1. Bonsoir,

                  Merci pour la rapidité apportée à ta réponse !

                  Je te propose un script plus épuré.

                  #!/bin/bash 
                  # Essai de menu / sous-menu 
                  
                  # Sélection - je n'ai laissé que deux options, la première ayant un sous-menu
                  PS3='Faites votre choix: '
                  options=("Commandes" "Exit")
                  select opt in "${options[@]}"
                  do
                  	if [ "$opt" == "Exit" ] ; then
                      break
                      fi
                  
                      case $opt in
                          "Commandes")
                           PS3='Quel choix ?'
                  	options=("pwd" "curl" "Quit")   
                  	select model in "${options[@]}" 
                  	do 		
                  		 case $model in
                  			"pwd")
                  				echo "pwd va s'afficher'"
                  				 pwd
                  				 break ;;
                  				 				 					                             
                  			"curl")           
                  				echo "curl va s'afficher"
                  				curl ifconfig.co/json    
                  				break;; 
                  			"Quit")  
                  			 exit
                  			;;   
                  		*) echo "invalid option $REPLY";;                      
                  		esac
                  	done             
                       exit   
                      esac
                  done  
                  
                  # afficher la date (autre commande pour vérifier la poursuite de l'installation) 
                  	 
                  	date +"Aujourd'hui, nous sommes le %A %d %B. Il est %T." 

                  En fait, je désire effectuer un choix (enfin, deux !) , à deux niveaux différents :

                  - le 1er niveau  (menu principal), entre les deux options "Commandes" et "Exit".

                  - le second, une fois l'option "Commandes" choisie, un autre choix intervient  entre "pwd", "curl" et "quit".

                  Mon objectif est de sélectionner "Commandes" puis "pwd" par ex. pour ensuite voir la commande suivante "date..." s'exécuter.

                  Lors de l'exécution, le choix "Commandes" "pwd" indique bien le chemin (pwd) comme attendu. Par contre, le programme s'arrête sans exécuter la commande suivante "date...".

                  Si je sélectionne "Exit" au démarrage, on sort du menu et le programme affiche la commande suivante "date..." (cf. ci-dessous)

                  1) commandes
                  2) Exit
                  Faites votre choix: 2
                  Aujourd'hui, nous sommes le jeudi 20 octobre. Il est 22:08:41. C'est le 293e jour de l'année.
                  
                  
                  ------------------
                  (program exited with code: 0)
                  Press return to continue
                  
                  

                  Comme tu as pu le constater, je ne suis pas un pro qui ne maîtrise pas tout. Je me suis inspiré de ce que j'ai pu glaner ici et là.

                  Merci.

                  1. Modérateur

                    Bonjour,

                    Peux-tu encore simplifier le code de sorte à ce qu'il soit minimal, et indiquer comment l'exécuter pour mettre en évidence ton problème (comportement attendu, comportement observé) ?

                    Pas besoin de télécharger quoi que ce soit, juste la structure de menu, des echo aux bons endroits.

                    Bonne chance