Menu using shell script

Hi,

I need to have a shell script for the below need.

  1. Menu with one heading and 4 options.
  2. the heading and 4 options are taken from a file.
    File entry
    ======
    Heading1|option1|option2|option3|option4|
    Heading2|option1|option2|option3|option4|
  3. the user entries must be captured in a file. say, response
  4. the user must get the next menu with heading 2, followed by 4 options.

Its a kind of survey.
I have it in python, but then wanted it in shell script.
Can anyone help me out here!

Thanks in advance.

What have you tried so far?
Have you search through the forums?

Well I thought this dynamic menu was going to be a simple example of looping through the input file that holds the menu structure, parsing the menu options line, then using the ksh select construct to prompt, but my select construct is not prompting and instead seems to be taking the input from the while loop read. Can anyone spot why this is happening? ksh93 on Solaris by the way. I thought maybe it was IFS but even if I reset it inside the main loop the same happens.

I recently posted an example of nested menus: http://www.unix.com/shell-programming-scripting/176103-trouble-getting-user-input-while-using-case-statement-unix.html\#post302595280 but this version differs with reading from the input file.

The menu input file called dynmenu.dat:

Heading1|h1_option1|h1_option2|h1_option3|h1_option4|
Heading2|h2_option1|h2_option2|h2_option3|h2_option4|
Heading3|h3_option1|h3_option2|h3_option3|h3_option4|

The script called dynmenu:

#!/usr/dt/bin/dtksh

typeset -r -x DYNMENUFILE=${0}.dat  # Menu file
typeset -r MAINPROMPT="Enter your selection: "
typeset -x PS3=$MAINPROMPT  # PS3 is the prompt for the select construct.
typeset -r RESPONSEFILE="${0}_${USER}.out"

> $RESPONSEFILE  # Create or clear the response file.

# Read lines from the menu file.
while IFS="|" read heading option1_in option2_in option3_in option4_in the_rest
do
  #  Loop until a correct answer is received.
  while :
  do
#    clear
    print "\n$heading\n"
    select option in $option1_in $option2_in $option3_in $option4_in
    do
      case $REPLY in   # REPLY is set by the select construct,
                       # and is the number of the selection.
        1) ;&  # Fall through
        2) ;&  # Fall through
        3) ;&  # Fall through
        4) print "${heading}: $option" >> $RESPONSEFILE
           break  # Break out 2 levels (get the next menu line)
           ;;
        *) # Always code for the unexpected.
           print "\nUnknown option [${REPLY}]"
           sleep 2
           break # redisplay the current menu
           ;;
      esac
    done  # End select
  done  # End while true
done < $DYNMENUFILE

exit 0

I get the menu for line 1 as expected, but immediately get the unknown option error with the reply being the entire line 2 from the file, etc.
Output:

$ dynmenu

Heading1

1) h1_option1
2) h1_option2
3) h1_option3
4) h1_option4

Unknown option [Heading2|h2_option1|h2_option2|h2_option3|h2_option4|]

Heading1

1) h1_option1
2) h1_option2
3) h1_option3
4) h1_option4

Unknown option [Heading3|h3_option1|h3_option2|h3_option3|h3_option4|]

Heading1

1) h1_option1
2) h1_option2
3) h1_option3
4) h1_option4

Heading1

1) h1_option1
2) h1_option2
3) h1_option3
4) h1_option4

Heading1

1) h1_option1
2) h1_option2
3) h1_option3
4) h1_option4

(Repeats until CTRL-C)
1 Like

You redirect stdin to read the file. 'select' reads an option from stdin. So 'select' ends up reading from the file.. Open it into another fd, and read from that.

I'm not sure if it's -u5 or -u 5.

exec 5<$DYNMENUFILE

while IFS="|" read -u5 option1_in option2_in option3_in option4_in the_rest
do
...
done

exec 5<&-
1 Like

DOH! Here's the corrected example then which works. Thanks Corona688!

#!/usr/dt/bin/dtksh

typeset -r -x DYNMENUFILE=${0}.dat  # Menu file
typeset -r MAINPROMPT="Enter your selection: "
typeset -x PS3=$MAINPROMPT  # PS3 is the prompt for the select construct.
typeset -r RESPONSEFILE="${0}_${USER}.out"

> $RESPONSEFILE  # Create or clear the response file.

# Create file desriptor 5 as input from the menu file, as the select construct needs to 
# read from stdin too.
exec 5<$DYNMENUFILE

# Read lines from the file descriptor associated with the menu file.
while IFS="|" read -u5 heading option1_in option2_in option3_in option4_in the_rest
do
  #  Loop until a valid option is entered.
  while :
  do
   clear
    print "\n$heading\n"
    select option in $option1_in $option2_in $option3_in $option4_in
    do
      case $REPLY in   # REPLY is set by the select construct,
                       # and is the number of the selection.
        1) ;&  # Fall through
        2) ;&  # Fall through
        3) ;&  # Fall through
        4) print "${heading}: $option" >> $RESPONSEFILE
           print "\nYou selected: ${heading}: $option"
           sleep 2
           break 2  # Break out 2 levels (get the next menu line)
           ;;
        *) # Always code for the unexpected.
           print "\nUnknown option [${REPLY}]"
           sleep 2
           break # redisplay the current menu
           ;;
      esac
    done  # End select
  done  # End while true
done  # End reading menu items

# Close fd 5.
exec 5<&-

exit 0

Instead of having 4 different cases with fall-through -- an option I've never seen in a shell before -- how about one case for all 4?

[1-4]) ... ;;

Sure, that's just my preference, I usually set it up like this for future changes where someone behind me (a less experienced developer perhaps) may need to add selection-specific code. If so, the framework if you will is ready for it.

I recall that is a habit I picked up when I coded switch statements in C years ago. Maybe it was a standard where I worked or something.

In C statements, you have to, since you can't put a range of values in one option...

Hey Guys,

That's impressive! It works perfect :slight_smile:
I have one more doubt? What if i want to store the option entered and not the string corresponding to the option in the out file?

Thanks in advance.

Just save $REPLY instead of $option:

     4) print "${heading}: $REPLY" >> $RESPONSEFILE
1 Like