-3

I am writing a bash script and want to add some simple validation tests. The input should be either 1 or 2 and the script should ask for input again until the correct input is given.

I was thinking of doing this using an until loop but it didn't work. I tied until [ $group -eq 1 ] (where $group is the value given by the user) but obviously I also need a 2nd input. How can I do this correctly?

3
  • 1
    Your question is quite unclear. Show your code Mar 3, 2016 at 10:44
  • 1
    We can't help if you don't show i) the exact code you used, what you show is a syntax error and ii) the error message you received. Please edit and clarify.
    – terdon
    Mar 3, 2016 at 11:03
  • @StéphaneChazelas I edited it to make it clearer. I was hoping the OP would do so instead.
    – terdon
    Mar 3, 2016 at 12:24

4 Answers 4

3
until
  printf 'Please enter 1 or 2: '
  IFS= read -r line || exit # exit on EOF
  [ "$line" = 1 ] || [ "$line" = 2 ]
do
  printf >&2 '"%s" is neither 1 nor 2, try again.\n' "$line"
done

With IFS= and -r, the input line is stored as-is in $line. You may want to omit the IFS= so that blanks (assuming you've not modified $IFS) are automatically stripped from the beginning or end of the input.

If the input has to be the 1 or 2 strings only, then you want to use =, not -eq.

If you want to allow other expressions of the 1 or 2 numbers, like 01, 1+1, 1.0, 100e-2, exp(0), RANDOM (sometimes), then you could use -eq, but note that not all shells accept all types of expressions above (ksh93 will accept them all, but bash would only accept things like 1 (leading and/or trailing blanks), or 0001 (leading zeros)), and that means you'll get error messages for inputs that don't form valid arithmetic expressions.

With shells that interpret arithmetic expressions, that's also dangerous as that can change variable values (like for an input like PATH=123) or even run arbitrary commands (like for inputs like a[0$(cmd>&2)]).

Depending on the shell, you could also get false positives on 18446744073709551617 or 4294967297 (or any other multiple of 232 or 264 + 1 or 2) as most shells use 64bit or 32bit integer numbers.

Whatever you do, do not use the -o and -a binary test/[ operators. Those should be banished as they make the parsing of the [ command potentially ambiguous and in practice unreliable (and are now marked obsolete in the POSIX spec).

For instance:

$ line='!' sh -c '[ "$line" = 1 -o "$line" = 2 ]'
sh: line 0: [: too many arguments

And remember to quote your variables ("$line", not $line which would undergo split+glob).

2
  • When you say With IFS= and -r, the input line is stored as-is in $line - What is the need to store as-is? Even if the line was split into words delimited by IFS won't read dump all the words into $line anyway? (because line is the only variable available) Mar 3, 2016 at 12:21
  • @the_velour_fog, see Understand "IFS= read -r line"? Mar 3, 2016 at 12:37
1

This should work, combine them with or (-o):

#init group variable
group=0
#read until value is either 1 or 2
until [ $group -eq 1 -o $group -eq 2 ]; 
    do 
    #your script...

    #read user input and put it in group
    read -p "Which group?" group 
done

You can also use || as or operator:

until [ $group -eq 1 ] || [ $group -eq 2 ]

Although in some cases, for example when you use the error code ($?) the first options is better, see this answer.

1
  • With the default value of IFS, that would accept inputs like 1 -o 1 or ! 0. I can't think of any reason why you would want to invoke the split+glob operator here. Mar 3, 2016 at 12:33
1
while IFS= read -r; do
    case "$REPLY" in
        1|2) break ;;
        *)   echo 'Error' >&2 ;;
    esac
done

This code snipped reads a value from standard input and then tests it against 1 and 2. If matching either string, the loop exits, otherwise a terse diagnostic message is produced and the loop continues.

-1

Just add the second condition to the expression using the or operator -o.

14
  • 2
    -o and -a [ binary operators should be avoided, they make the parsing of the [ command potentially ambiguous and in practice unreliable. Use [ "$i" = 1 ] || [ "$i" = 2 ] instead. Mar 3, 2016 at 10:47
  • @StéphaneChazelas Can you provide any sources for this claim? While your alternative seems to be valid, I consider it a solution to a problem most people don't have...
    – Murphy
    Mar 3, 2016 at 10:50
  • 1
    See link in my answer. Mar 3, 2016 at 11:52
  • @Murphy: [ "!" = 1 -o 1 = 1 ] raise error in most shells except zsh.
    – cuonglm
    Mar 3, 2016 at 12:24
  • 1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .