4

I'm trying to enable only certain options when using the read command, and to exit the script if a wrong possibility was entered.

Tried many possibilities (array, variables, syntax change), but I'm still stuck with my initial problem.

How do I test the input of the user and allow \ disallow to run the rest of the script?

#!/bin/bash

red=$(tput setaf 1)
textreset=$(tput sgr0) 

echo -n 'Please enter requested region > '
echo 'us-east-1, us-west-2, us-west-1, eu-central-1, ap-southeast-1, ap-northeast-1, ap-southeast-2, ap-northeast-2, ap-south-1, sa-east-1'
read text

if [ -n $text ] && [ "$text" != 'us-east-1' -o us-west-2 -o us-west-1 -o eu-central-1 -o ap-southeast-1 -o ap-northeast-1 -o ap-southeast-2 -o  ap-northeast-2 -o ap-south-1 -o sa-east-1 ] ; then 

echo 'Please enter the region name in its correct form, as describe above'

else

echo "you have chosen ${red} $text ${textreset} region."
AWS_REGION=$text

echo $AWS_REGION

fi
1
6

Why don't you use case?

case $text in 
  us-east-1|us-west-2|us-west-1|eu-central-1|ap-southeast-1|etc) 
         echo "Working"
  ;;

  *)
         echo "Invalid option: $text"
  ;;
esac 
2
  • Great answer! simple and easy, I've even added an error message in case anyone just press 'enter' (which display $text as an empty field- so although it is the correct behavior, it seems like an error)(stackoverflow.com/questions/17575392/…) – Busted Jul 26 '16 at 11:15
  • You can add this clause "") echo "Please enter some data" ;;. Note that it needs to come first than *) clause. – Luciano Andress Martini Jul 26 '16 at 14:58
5

Why not make the life of the user a bit easier by not requiring them to type the name of the region at all?

#!/bin/bash

echo "Select region"

PS3="region (1-10): "

select region in "us-east-1" "us-west-2" "us-west-1" "eu-central-1" \
    "ap-southeast-1" "ap-northeast-1" "ap-southeast-2" \
    "ap-northeast-2" "ap-south-1" "sa-east-1"
do
    if [[ -z $region ]]; then
        echo "Invalid choice: '$REPLY'" >&2
    else
        break
    fi
done

echo "You have chosen the '$region' region"

If the user types in anything other than a valid numerical option from the list, the value in $region will be an empty string and we display an error message. If the choice is valid, the loop exits.

Running it:

$ bash script.sh
Select region
1) us-east-1         4) eu-central-1     7) ap-southeast-2  10) sa-east-1
2) us-west-2         5) ap-southeast-1   8) ap-northeast-2
3) us-west-1         6) ap-northeast-1   9) ap-south-1
region (1-10): aoeu
Invalid choice: 'aoeu'
region (1-10): .
Invalid choice: '.'
region (1-10): -1
Invalid choice: '-1'
region (1-10): 0
Invalid choice: '0'
region (1-10): '
Invalid choice: '''
region (1-10): 5
You have chosen the 'ap-southeast-1' region
5
  • Awesome answer ! I'll be sure to check this out. In my case , since it is a part of a bigger script with many implication- (money for starters), I don't want to hear later on- oopsy.. did I set up the whole environment in another region? - didn't mean too.. – Busted Jul 25 '16 at 15:12
  • 1
    A simpler way to check for a valid entry is to check $region against the empty string. See also $PS3 to change the prompt. Better to issue the error on stderr. – Stéphane Chazelas Jul 25 '16 at 15:22
  • @StéphaneChazelas Much appreciated! Will edit. – Kusalananda Jul 25 '16 at 15:26
  • @StéphaneChazelas Sorry to ask a question here, but do you have any clues as to why the PS3 prompt is actually displayed on stderr? – Kusalananda Jul 25 '16 at 15:29
  • 2
    @Kusalananda, same for the choice or for bash's prompt or read -p. It's not standard output of your script, not something that you would pipe to something else. It's only for user interaction. If you redirect the script's output, you'll still want to see those prompts. – Stéphane Chazelas Jul 25 '16 at 15:46
4

The problem is with this:

[ "$text" != 'us-east-1' -o us-west-2 -o ... ]

The -o means or and you need a full condition, so it would be

[ "$text" != 'us-east-1' -o "$text" != 'us-west-2' -o ... ]

See how we're having to test $text each time?

Your logic is also wrong; you want -a (and); if it's not "us-east-1" and it's not "us-west-2" and it's not...

So

[ "$text" != 'us-east-1' -a "$text" != 'us-west-2' -a ... ]

There are other ways of doing this sort of test; some of which are merely "personal preference". However this syntax should get you going and follows the form and structure of your original.

1
  • Thank you for explaining it, It seems I did messed up the logic- oddly enough I did try it like you said, I guess I was too tired.. – Busted Jul 26 '16 at 11:24
2

I really can't stand scripts that ask me questions rather than allow me to use command line options (easier to edit and re-use previous command lines, easier to use in a script), so I'd use getopts like this:

#!/bin/bash

regions=(us-east-1 us-west-2 us-west-1 eu-central-1 ap-southeast-1
         ap-northeast-1 ap-southeast-2 ap-northeast-2 ap-south-1
         sa-east-1)

region=0  # default to region 0, us-east-1

usage() {
  [ -n "$1" ] && printf '%s\n\n' "$@"

  echo "Usage: $0 [ -r region-number|\"list\"] ..."
  # print more help here
  exit 1
}

list_regions() {
  [ -n "$1" ] && printf '%s\n\n' "$@"

  printf '%s\n' "${regions[@]}" | cat -n
  exit 1
}

check_region() {
  [ "$region" == "list" ] && list_regions
  [[ ! "$region" =~ ^[0-9]+$ ]] && usage "Region code must be numeric"

  region=$((region - 1))  # bash arrays are zero-based
  [ -z "${regions[$region]}" ] || \
    [ "$region" -lt 0 ] && list_regions "Unknown region code"
}

while getopts 'r:h' opt ; do
  case "$opt" in
     r) region="$OPTARG" ; check_region ;;

     h) usage ;;
     *) usage ;;
    esac
done
shift $(($OPTIND - 1))

# do whatever with "$region" and/or "${regions[$region]}"
echo region="${regions[$region]}"

Some example runs:

$ ./busted.sh 
region=us-east-1
$ ./busted.sh -r
./busted.sh: option requires an argument -- r
Usage: ./busted.sh [-r region-number|"list"] ...
$ ./busted.sh -r list
     1  us-east-1
     2  us-west-2
     3  us-west-1
     4  eu-central-1
     5  ap-southeast-1
     6  ap-northeast-1
     7  ap-southeast-2
     8  ap-northeast-2
     9  ap-south-1
    10  sa-east-1
$ ./busted.sh -r 99
Unknown region code

     1  us-east-1
     2  us-west-2
     3  us-west-1
     4  eu-central-1
     5  ap-southeast-1
     6  ap-northeast-1
     7  ap-southeast-2
     8  ap-northeast-2
     9  ap-south-1
    10  sa-east-1
$ ./busted.sh -r 7
region=ap-southeast-2
2
  • Hellow friend, very nice script! +1 – Luciano Andress Martini Aug 1 '16 at 11:40
  • thanks. on looking at this again, it's a little too verbose for my liking so I'd probably make the "Unknown region code" error message just say use '-r list' for a list rather than printing the entire list. and use column or something in the list_regions function so that the list didn't take up so many output lines. – cas Aug 1 '16 at 12:01
1

You could do something like this:

valid=(foo bar doo)
echo enter something, valid values: "${valid[@]}"
read text
ok=0
for x in "${valid[@]}" ; do 
    if [ "$text" = "$x" ] ; then ok=1 ; fi ; 
done 
echo is it ok: $ok

The valid values are saved in a bash array, which can be used both for display and to test the input string.

Apart from the fact that -o in test need a full condition, there are also arguments that one shouldn't use

[ "$x" != "foo" -a "$x" != "bar" ] 

but instead

[ "$x" != "foo" ] && [ "$x" != "bar" ] 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.