LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (https://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   menu with while loop (https://www.linuxquestions.org/questions/linux-newbie-8/menu-with-while-loop-911771/)

greenpool 11-03-2011 10:06 PM

menu with while loop
 
I'm trying create a menu with a while loop in the following manner

Code:

#!/bin/bash


echo -n "

1. healthchk
2. bpipwd
3. cust_usage_check
4. other scripts automatic

enter choice [1 | 2 | 3 | 4 ]: "
read numchoice



while [ !($numchoice -ge 1) && !($numchoice -le 4) ]
do
echo -n "you entered incorrect, try again: "
read numchoice
done

error i'm getting is:

[: missing `]'


I suspect its something to do with my syntax. can someone please tell me what i'm doing wrong, thanks!

mladoux 11-03-2011 11:58 PM

while loops don't work quite like that. This guide will show you where you've gone wrong:

http://tldp.org/LDP/abs/html/loops1.html

smilesavvy 11-04-2011 12:19 AM

Don't use While loop for menu. use " select ".
Code:

#!/bin/bash
PS3="Choose the correct option:  "
select i in healthchk bpipwd cust_usage_check other_scripts_automatic
do
  case $i in
  healthchk) echo "you selected option 1";;
  bpipwd) echo "you selected option2";;
  cust_usage_check) echo "you selected option 3";;
  other_scripts_automatic) echo " you selected option 4";;
  *) echo " you have entered wrong option, please choose the correct option from the listed menu."
    sleep 5
    exit;;
  esac
done


-- Ramesh Jonathan Godishela

devUnix 11-04-2011 12:43 AM

Here is what I use:


Code:

#!/bin/bash
numchoice=1
while [ $numchoice != 0 ]; do
 echo -n "
 1. healthchk
 2. bpipwd
 3. cust_usage_check
 4. other scripts automatic
 0. Exit

 enter choice [1 | 2 | 3 | 4 ]: "
 read numchoice
 case $numchoice in
        "1" ) ./healthchk.sh ;;
        "2" ) ./bpipwd.sh ;;
        "3" ) ./cust.sh ;;
        "4" ) ./other.sh ;;
        "0" ) break ;;
        * ) echo -n "You entered an incorrect option. Please try again." ;;
 esac
done


Here is a sample run of the above script:

Code:

$ /bin/bash h.sh

 1. healthchk
 2. bpipwd
 3. cust_usage_check
 4. other scripts automatic
 0. Exit

 enter choice [1 | 2 | 3 | 4 ]: 1
h.sh: line 14: ./healthchk.sh: No such file or directory

 1. healthchk
 2. bpipwd
 3. cust_usage_check
 4. other scripts automatic
 0. Exit

 enter choice [1 | 2 | 3 | 4 ]: 5
You entered an incorrect option. Please try again.
 1. healthchk
 2. bpipwd
 3. cust_usage_check
 4. other scripts automatic
 0. Exit

 enter choice [1 | 2 | 3 | 4 ]: 0


You can remove these double quotes:

Code:

        "1" ) ./healthchk.sh ;;
        "2" ) ./bpipwd.sh ;;
        "3" ) ./cust.sh ;;
        "4" ) ./other.sh ;;
        "0" ) break ;;

but do not quote the star (*).

You see:

Code:

h.sh: line 14: ./healthchk.sh: No such file or directory
because I did not write the healthchk.sh script for you. ;) You do that!

Telengard 11-04-2011 12:55 AM

First, are you using Bash's builtin [, or are you using an external implementation of [? Here's how to find out; if it is Bash's [ then it doesn't understand the --help option.

Code:

tmp$ [ --help
bash: [: missing `]'

Note that it was Bash which responded with an error message.

Now in your script, here's the problem line:

Code:

while [ !($numchoice -ge 1) && !($numchoice -le 4) ]
#                            ^ command list separator

The problem is that && is interpreted by Bash as a command list separator. That's why the closing ] isn't recognized. Everything after && is treated as a separate stanza of the command list.

anon258 11-04-2011 04:32 AM

change line with while definition to

while [ $numchoice -lt 1 -a $numchoice -gt 4 ]

David the H. 11-04-2011 07:37 AM

Numeric tests should properly be done in bash using ((..)), and string tests in [[..]]. The only time you need to use the old single bracket [ is when you're writing posix-compliant scripts for systems without bash.

http://mywiki.wooledge.org/ArithmeticExpression
http://mywiki.wooledge.org/BashFAQ/031

select is good for simple menus, but it's often necessary to roll your own for more complex stuff. Most experienced scripters tend to emulate select and put the menu in a never-ending while true loop, using an if or case statement inside to evaluate the choice made, and break commands to exit the loop when desired.

Untested, but to demonstrate the principle...
Code:

#!/bin/bash

menu='
1. healthchk
2. bpipwd
3. cust_usage_check
4. other scripts automatic

enter choice [1 | 2 | 3 | 4 ]: '

while true; do

        echo -n "$menu"
        read numchoice

        if (( numchoice >= 1 && numchoice <= 4 )); then
                echo -n "you entered incorrect, try again: "
        else
                echo "you chose $numchoice.  goodbye!"
                break
        fi

done


Telengard 11-04-2011 01:31 PM

I just tried to answer OP's qyestion as directly as possible. My previous reply doesn't seem very clear. Here is a much better explanation of the problem I see in OP's code:

BashPitfalls - Greg's Wiki:6. [ "$foo" = bar && "$bar" = foo ]

All the replies I see here now bring up other important points too, and IMHO OP should heed them.

HTH

PTrenholme 11-04-2011 04:33 PM

:scratch: David the H., I think you forgot the $ signs in your if statement.

This worked for me:
Code:

$ cat tmp.bash
#!/bin/bash
while [ $# -gt 0 ]
do
  numchoice=$1
  shift
  if (( $numchoice >= 1 )) && (($numchoice <= 4 ))
  then
    echo Yes
  else
    echo No
done
$ ./tmp.bash 0 1 2 3 4 5 6 7 8 9 10
No
Yes
Yes
Yes
Yes
No
No
No
No
No
No


crts 11-04-2011 06:56 PM

Quote:

Originally Posted by PTrenholme (Post 4516073)
:scratch: David the H., I think you forgot the $ signs in your if statement.

Actually, he did not. Variables do not have to be preceded by $ inside ((...)). At least not in bash v4.1.5. I know, it is inconsistent.
There is more confusion about ((...)). Consider the following:
Code:

num=3
if (( num = 5 )); then echo true;else echo false;fi
true
echo $num
5

As you can see, the '=' does an assignment to num. The 'classic' [[ ... ]] treats it as comparison operator:
Code:

num=3
if [[ $num = 5 ]]; then echo true;else echo false;fi
false
echo $num
3

If you want to compare the variable for equality inside (( ... )) you have to use '=='
Code:

num=3
if (( num == 5 )); then echo true;else echo false;fi
true
echo $num
3

@OP: Sorry for taking this a bit off topic.

PTrenholme 11-05-2011 12:14 PM

Yes, you're correct. :redface:

In fact, both (( numchoice >= 1 )) && (( numchoice <= 4 )) and (( numchoice >= 1 && numchoice <= 4 )) work as the object of the if with bash 4.2.10.

I plead being set in my ways after 70 odd years . . . :)

David the H. 11-05-2011 01:23 PM

That's right. In any arithmetic environment field, the only recognized strings are digits and mathematical operators. So when the shell sees an alphabetical string, it assumes it's a variable and automatically expands it before evaluation. It saves a bit of typing and clutter, at the least.

As far as I know, this is true of all bourne-based shells. It's likely even a posix specification, although I haven't confirmed that.

By the way, not only is "=" a comparison operator inside the [ and [[ bracket tests, it's a string comparison; the two sides will be evaluated according to their alphanumeric/ascii order values. For an arithmetic comparison, you must use "-eq" and similar. That's one reason the ((..)) test is recommended, it makes it clear at a glance that it's an arithmetic operation, and lets you use the more natural math operators you're used to (with the exception of the "=/==" difference explained above).


All times are GMT -5. The time now is 02:34 PM.