12

I am trying to implement a simple menu using the select command. The script (for testing purposes) is as follows:

#!/bin/bash
echo "*******************"
PS3='Select an option and press Enter: '
options=("apache" "named" "sendmail")
select opt in "${options[@]}"
do
  case $opt in
        "apache")
          date
          ;;
        "named")
          echo "test"
          ;;
        "sendmail")
          echo "test 2"
          ;;
        *) echo "invalid option";;
  esac
done
echo "*********************"

The script is not recognizing any valid inputs I give and always prints the "invalid option" message. What am doing wrong in this script?

ilkkachu
  • 138,973
Sumod
  • 597
  • 2
  • 6
  • 9

2 Answers2

14

Your menu shown by select will look like:

1) apache
2) named
3) sendmail
Select an option and press Enter: 

At this time, you enter "1" or "2" or "3": you don't type the word.

Also, select will loop until it sees a break command, so you want this:

  case $opt in
        "apache")
          date
          break
          ;;
        "named")
          echo "test"
          break
          ;;
        "sendmail")
          echo "test 2"
          break
          ;;
        *) echo "invalid option";;
  esac

If you wanted to allow the user to enter either the number or the word, you could write this:

select opt in "${options[@]}"; do
  case "$opt,$REPLY" in
    apache,*|*,apache)     do_something; break ;;
    named,*|*,named)       do_something; break ;;
    sendmail,*|*,sendmail) do_something; break ;;
  esac
done

The comma has no syntactical significance, it's just there to be able to pattern match on either the $REPLY variable (which is what the user actually typed) or the $opt variable

glenn jackman
  • 85,964
  • The select command needs a break, not case. – glenn jackman Sep 14 '11 at 18:09
  • Thanks! I did not realize that it was numerical options. Duhh! – Sumod Sep 15 '11 at 10:45
  • Hah, I too program in C and, you do need a break there in every section. However, you will never need a break in a case...esac block in a shell script. It can happen so easily to get them mixed up. :-) – syntaxerror Nov 20 '14 at 11:05
  • 1
    @syntaxerror, you're right, you don't need break for case. However, you need a break to escape the select loop. – glenn jackman Nov 20 '14 at 12:55
  • @glenn jackman I would like to have this turned into a more helpful comment (see next). So it would be great if you agreed on deleting your comment from 12:55. I will do the same with mine, of course. – syntaxerror Nov 23 '14 at 16:20
  • It is particularly important to note that the break statements in the code snippet shown are not for case, but in order to prematurely exit that select block! While you will (um, generally) need a break even for each case in a switch-case block in C language, for shell scripts you never will. So if there was no select, all the break statements could be omitted as well (in shell scripting). Just wanted to point that out, as it can easily be misread. – syntaxerror Nov 23 '14 at 16:23
  • Clarification: break does not "prematurely" exit the select command -- select will loop forever unless "broken". – glenn jackman Nov 23 '14 at 19:11
3

Your $opt variable will be set to the option word corresponding to the number that the user inputs. If you want to look at what the user actually typed, look at $REPLY.

Kusalananda
  • 333,661
  • 1
    Given that the case compares $opt against "apache", "named" and "sendmail", it is correct and nothing has to be changed. – manatwork Sep 14 '11 at 14:13
  • @manatwork, unless the OP wants the user to type in the words "apache", "named" or "sendmail" rather than pressing 1, 2 or 3. – Kusalananda Sep 14 '11 at 14:38
  • Interesting. $REPLY holds 1/2/3, and $opt holds apache/named/sendmail – Peter.O Sep 15 '11 at 06:50
  • @fred, yes, $REPLY is whatever the user typed, and if it was a valid number then $opt will be the word corresponding to that number. – Kusalananda Sep 15 '11 at 06:53