1

I have a bash script that will take one argument: a product ID. The product ID can be in one of two formats: all numbers, or a mix of letters, numbers, and underscores. Depending on which type of ID is entered, the script will handle it in a slightly different way.

Right now, I'm using getopts with one flag for each subtype to distinguish between which type of product ID I'm going to be using in the script. For example:

./myscript -n 1034596

or

./myscript -v AB_ABCD_12345

With a simplified version of the script looking like this:

#!/bin/bash
while getopts ":n:v:" opt; do
  case $opt in
    n)
      echo "This is a numbers only ID." >&2
      ;;
    v)
      echo "This is a letters, numbers, underscore ID" >&2
      ;;
  esac
done

Since the formats are static, that is, the first type of ID will never be anything but numbers, is there any way to automatically distinguish between the two types of IDs and handle them appropriately without the need for the -n or -v flag? So, I could just enter ./myscript 1034596 and the script will know that since the argument contains nothing but numbers it should process it a specific way.

michaelmichael
  • 13,755
  • 7
  • 54
  • 60

8 Answers8

5
#!/bin/bash

shopt -s extglob

case "$1" in
  +([0-9]) ) echo "This is a numbers only ID." >&2
    ;;
  +([a-zA-Z0-9_]) ) echo "This is a letters, numbers, underscore ID" >&2
    ;;
  *) echo "Unrecognized Product ID" >&2
esac
Thedward
  • 1,432
  • 9
  • 8
4

Pure Bash 3.X

#!/bin/bash
if [[ "$1" =~ ^[0-9]+$ ]]; then
  echo "This is a numbers only ID." >&2
else
  echo "This is a letters, numbers, underscore ID" >&2
fi

Output

$ ./argtype.sh 1034596
This is a numbers only ID.
$ ./argtype.sh AB_ABCD_12345
This is a letters, numbers, underscore ID
SiegeX
  • 135,741
  • 24
  • 144
  • 154
  • 1
    Quoting `$1` should not be needed, even if it contains spaces. `[[` does the Right Thing. – sorpigal Dec 09 '10 at 19:03
  • Needed or not, I *always* side on the error of caution and quote my variables to prevent word splitting in the same way that I always add parentheses to enforce order of precedence even when not necessary. The downside of not having them when you needed them far outweighs the 2 extra keystrokes it takes to put them there. – SiegeX Dec 09 '10 at 21:20
3

Try this code:

#!/bin/bash
if [ $1 -eq $1 2> /dev/null ]; then
    echo number
else
    echo not number
fi

The output on your given input is:

brent@battlecruiser:~$ ./test2 1034596
number
brent@battlecruiser:~$ ./test2 AB_ABCD_12345
not number
Brent Newey
  • 4,479
  • 3
  • 29
  • 33
  • interesting, this is the only answer that doesn't use grep. how does the if statement work? – michaelmichael Dec 09 '10 at 17:53
  • `-eq` is a number comparison operation. If you ever compare a non-number of any kind, it will return false, ergo numbers will always be equal to themselves, and nothing else will be "equal" to itself using the `-eq` comparison. – Brent Newey Dec 09 '10 at 17:58
  • 3
    Note what happens if you take the `2> /dev/null` out, you will receive the error "./test2: line 2: [: 12345_AB: integer expression expected". – Brent Newey Dec 09 '10 at 18:00
2

There is a perhaps slightly more readable way with bash, but this can be done perfectly reasonably in pure portable sh.

unset LC_COLLATE
case $1 in
  *[!0-9A-Z_a-z]*) echo 1>&2 "Invalid product ID"; exit 2;;
  *[!0-9]*) echo "alphanumeric product ID";;
  *) echo "numeric product ID";;
esac
Community
  • 1
  • 1
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
1

Take a look at accepted the answer to this question. Around line 8 is a regex technique that you can adapt.

Community
  • 1
  • 1
GinoA
  • 732
  • 1
  • 6
  • 11
1
#!/bin/sh

arg="$1"
output=`echo "$1" | grep -o "[0-9]\+"`
if [ "$output" == "$arg" ]; then
  echo "Numbers only"
else
  echo "Mixed"
fi
khachik
  • 28,112
  • 9
  • 59
  • 94
1

You could use grep to validate if the string passed in is a number:

#!/bin/bash
echo $1 | grep -q "^[0-9]*$"
if [ $? ]; then
    echo "Number"
else
    echo "Not a number"
fi

With comments updates:

#!/bin/bash

if grep -q "^[0-9]*$" <<< "$1"; then
    echo "Number"
else
    echo "Not a number"
fi
Rod
  • 52,748
  • 3
  • 38
  • 55
  • You can use a subshell to get the test into one line. Grep's return value will propagate to if: if $( echo "$1" | grep -q '^[0-9]*$' ); then – Patrick McMurchie Dec 09 '10 at 17:52
  • You could say `if echo $1 | grep -q "^[0-9]*$" ; then` and save yourself some effort. The result is the same: if is given the return code of the final program in the pipeline. You also don't need echo and pipes if you're using bash, you can use a here-string. `if grep -q "^[0-9]*$" <<< "$1" ; then` – sorpigal Dec 09 '10 at 18:55
1

In Bash 3.2 or greater:

pattern1='^[0-9]+$'
pattern2='^[0-9a-zA-Z_]+$'
if [[ $1 =~ $pattern1 ]]
then
    echo "argument consists only of digits and is validated"
else
    echo "argument contains other characters"
    if [[ $1 =~ $pattern2 ]]
    then
        echo "argument is validated"
    else
        echo "argument contains invalid characters"
    fi
 fi
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439