1

I am writing a script in bash which will prompt the user for two inputs. These inputs are assigned to the variables 'TO_SCHEMA' and 'FROM_SCHEMA' respectively.

I need a way to verify proper input. My requirements are as follows:

Each variable will have 3 acceptable values. They are the same values for both variables, but both variables must be in this list of three, and they cannot be the same value.

So if the values are 'myco', 'myco_int', and 'teambatch', then both variables must be one of those values, but they can't be the same.

${TO_SCHEMA} = myco && ${FROM_SCHEMA} = myco_int
   Pass

${TO_SCHEMA} = myco_int && ${FROM_SCHEMA} = myco_int
   Fail

${TO_SCHEMA} = mco && ${FROM_SCHEMA} = myco_int
   Fail

${TO_SCHEMA} = myco && ${FROM_SCHEMA} = donkey
   Fail

How can I accomplish this?

I began with an if statement full of AND and OR operators, but they got ugly fast. My experience with regex is limited, and my experience with sed and awk is non-existent, but I'm willing to learn and try any of that. Any help would be appreciated.

EDIT:

I should also mention that this script is just for a somewhat small tedious one off task I have to do a lot at work that I would love to automate. If I'm not the one using it, then someone on my team will be. So this input checking is a want and not a need. It's not the end of the world if the script breaks because of bad input. I would just like it to handle bad input more elegantly.

EDIT AGAIN: I appreciate everyone's suggestions, but I have to make some clarifications. The values won't actually be schema 1,2 and 3. I'm not allowed to provide proper names for security reasons, but I'm changing them to values more similar to the real ones.

6
  • homework assignment? Commented Mar 1, 2016 at 23:12
  • ... there is a very limited list of possible combinations, if I got things right. So probably you should just represent a list of those and let the user choose. Commented Mar 1, 2016 at 23:15
  • @Lizardx Haha, no not a homework assignment. Just a sad SE who hasn't done a lot of scripting before, but this will be a nice simple script that will save me a lot of time, and allow others to complete its task if I get hit by a bus or something. Commented Mar 1, 2016 at 23:19
  • @mikyra I wish, but this is very much back end only. This script is going to run on a redhat virtual machine CLI. I'm writing the damn thing using VI. So no mouse, no gui, no drop downs. Commented Mar 1, 2016 at 23:21
  • [[ $TO_SCHEMA != $FROM_SCHEMA && $TO_SCHEMA =~ ^schema(1|2|3)$ && $FROM_SCHEMA =~ ^schema(1|2|3)$ ]] Commented Mar 1, 2016 at 23:46

2 Answers 2

1

The simplest solution requires bash 4.3. You simply store the valid inputs as the keys of an associative array, then use the -v operator to check if a given input is defined as a key.

declare -A valid
# Values don't matter, as long as the key exists.
# I put spaces in the keys just to show it's possible
valid[schema 1]=
valid[schema 2]=
valid[schema 3]=

if [[ $FROM_SCHEMA != $TO_SCHEMA && -v valid[$FROM_SCHEMA] && -v valid[$TO_SCHEMA] ]]; then
    # inputs pass
else
    # inputs fail
fi

In earlier 4.x versions, you can check for undefined values in an associative array slightly differently.

declare -A valid
# Now, we need to make sure the values are non-null, but
# otherwise it doesn't matter what they are
valid[schema 1]=1
valid[schema 2]=1
valid[schema 3]=1
if [[ $FROM_SCHEMA != $TO_SCHEMA && -n ${valid[$FROM_SCHEMA]} && -n ${valid[$TO_SCHEMA]} ]]; then
    # inputs pass
else
    # inputs fail
fi

Prior to bash 4, with no associative arrays, you can fall back to scanning the list of valid inputs stored in a regular array.

valid=("schema 1" "schema 2" "schema 3")
if [[ $TO_SCHEMA == $FROM_SCHEMA ]]; then
    # inputs fail
else
    ok_count=0
    # We've already established that TO and FROM are different,
    # so only at most one per candidate can match.
    for candidate in "${valid[@]}"; do
        if [[ $TO_SCHEMA == $candidate || $FROM_SCHEMA == $candidate ]]; then
            ok_count+=1
        fi
    done
    if (( ok_count == 2 )); then
        # inputs pass
    else
        # inputs fail
    fi
fi
Sign up to request clarification or add additional context in comments.

2 Comments

This is pretty much exactly what I was looking for. I know I could have figured out a way to do it, but I was hoping for help with a practical code efficient way to do it. I'll test and see which works best tomorrow. Thank you!
I had to go with door number 2 because we don't use the most recent version of bash, but it works! Thanks!
0

Note, quick and dirty, lacking in elegance, but works. This is assuming your schema1 answers are accurate. And that I read your question right. you'd be replacing the [your input 1] etc with wherever you are getting the data from, it's a read of user input right? Why not just ask them to select it from a list?

values='1 2 3'
result1=''
result2=''

input1=[your input 1]
input2=[your input 2]
# force to lower case
input1=$(tr '[A-Z]' '[a-z]' <<< "$input1" )
input2=$(tr '[A-Z]' '[a-z]' <<< "$input2" )

for item in $values
do
    if [[ -n $( grep -E "^schema$item$" <<< $input1 ) ]];then
        values=$(sed "s/$item//" <<< $values )
        result1=$input1
    fi
done
for item in $values
do
    if [[ -n $( grep -E "^schema$item$" <<< $input2 ) ]];then
        result2=$input2
    fi
done

if [[ -z $result1 ]];then
    echo 'Your first entry is not right: ' $input1
elif [[ -z $result2 ]];then
    echo 'Your second entry is not right: ' $input2
else
    echo 'Valid data'
fi

Note that if you wanted to just test for the literal full string, you'd make it:

values='schema1 schema2 schema3'

then remove the 'schema' from the grep test, which would then just look for the full string thing. And sed would just remove the found item from the list of values.

If you are relying on user typed input, you must force it to lower to avoid spastic user actions or do a case insensitive pattern match with grep, but it's best to force it to lower before testing it.

input1=$(tr '[A-Z]' '[a-z]' <<< "$input1" )

2 Comments

Wow, that's.... I have a lot of Googling to do. Thank you for your input though! It looks better than whatever case statement I was gonna come up with. I'll explore this solution further when I'm back at work tomorrow.
The logic is easy to understand but maybe not easy to read. You start with a list of the numbers, the string part is static, if it wasn't, you'd just replace the literals in the values list. Then you test that the input, ideally trimmed of spaces etc, is an exact match for 'schema' + [number]. If it's a match, you remove the number from the list of possible values, since you found it in the first test. On the second test, you simply see if that string + number exists in the second one. Since the first test removed the found number, you are only left with the non found numbers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.