3 Comments

Parsing Arguments in Bash (getopts)

Have you ever passed several parameters to a command like this: ls -lah, and thought “I wish my bash scripts could parse command line parameters like that.” Allow me to introduce you to a bash function named getopts. Reader, meet getopts; getopts, meet reader.

The function getopts iterates through all command line parameters, evaluating if they match an expected parameter set. It takes two arguments: a string representing allowed parameters and a variable name to use while iterating through arguments. Here’s an example that should explain things:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/bin/bash
#
#file name: getopts.sh
#
#Sample script that takes two parameters, a, b, c, and v using getopts.
#Parameters a and c are simple on/off switches
#The b parameter takes a value.
#The v parameter will be used to keep track of a verbosity level;
#  more '-v's results in a higher verbosity level
#

usage=$(
cat <<EOF
$0 [OPTION]
-a          set the "a" flag
-b VALUE    set "b" argument to VALUE
-c          set the "c" flag
-v          increase verbosity
                (-v = verbosity level 1; -vv = verbosity level 2; ...)
EOF
)
#define default values
AFLAG_DEFAULT="off"
BFLAG_DEFAULT=""
CFLAG_DEFAULT="off"

verbosity=0

#getopts returns success if an option is found and failure otherwise
#so looping on it results in parsing all command line parameters
#ab:cv is how we represent the flags to be accepted
while getopts "ab:cv" OPTION; do
  case "$OPTION" in
    a)
      AFLAG="on"
      ;;
    b)
      #the colon after b in the args string above signifies that
      #  b should be accompanied with a user-defined value.
      #that value will be stored in the OPTARG environment variable
      BFLAG="$OPTARG"
      ;;
    c)
      CFLAG="on"
      ;;
    v)
      #each -v should increase verbosity level
      verbosity=$(($verbosity+1))
      ;;
    *)
      echo "unrecognized option"
      echo "$usage"
      ;;
  esac
done

#setting default values:
#this command will set VARIABLE to DEFAULT_VALUE if it is currently 
#  undefined, then return VARIABLE
#${VARIABLE=DEFAULT_VALUE}
#use a : to prevent bash from attempting to execute the value of 
#  VARIABLE when it is returned
: ${AFLAG=$AFLAG_DEFAULT}
: ${BFLAG=$BFLAG_DEFAULT}
: ${CFLAG=$CFLAG_DEFAULT}
#verbosity doesn't need a default value; it was initialized to 0 above.

#show the values as read in by the flags
cat <<EOF
a: $AFLAG
b: $BFLAG
c: $CFLAG
verbosity: $verbosity
EOF

Here are some sample runs to demonstrate the flexibility in how arguments can be passed:

Everything as a separate flag

1
2
3
4
5
$ ./getopts.sh -a -b aardvark -c -v -v -v
a: on
b: aardvark
c: on
verbosity: 3

All flags grouped together for the same effect (order does not matter)

1
2
3
4
5
$ ./getopts.sh -vvvacb aardvark
a: on
b: aardvark
c: on
verbosity: 3

Some flags grouped, some separated; default values used for missing arguments

1
2
3
4
5
$ ./getopts.sh -av -b aardvark
a: on
b: aardvark
c: off
verbosity: 1