05. Shell Variables and Command Line Arguments (Part 2)

Command Line Arguments

  • When a script name is typed on the command line, the shell interprets the script as an executable, just like it interprets a Linux utility
  • Therefore, the shell can accept command line arguments for the script, just like it does for a utility
  • For example:
    • cp fileA fileB fileA and fileB are arguments for cp
    • myScript dirA fileB dirA and fileB are arguments for myScript
  • When the shell interprets the command line, it stores each command line argument into special variables that the programmer can access
  • These variable names are also single characters, either a punctuation or a numeric symbol

Command Line Variable 0

  • The first word in a command line is interpreted to be an executable and is stored in the variable 0
  • For example, the following is typed on the command line:

myScript dirA fileB

  • In the script myScript is the line of code:

echo $0 # will print myScript, the name of the script

  • The 0 variable is useful when you need to refer to the name of your script, such as when printing an error message to the user
  • For example:

if [ $# -ne 2 ]

then

echo Usage: $0 file directory

exit 1

fi

  • If the number of arguments (stored in the # variable, next slide) is not 2, then the Usage message is printed to show the user how to use the script: the script is called with a file and a directory as arguments

Command Line Variable #

  • The shell keeps a count of the number of command line arguments and stores it in the variable #
  • For example, the following is typed on the command line:
    • myScript dirA fileB
  • In the script myScript is the line of code:
    • echo $# # will print 2
  • Note that # contains the number of arguments and does not include the actual executable in the count. In the example above, # is 2, even though 3 words are typed in
  • The # variable is useful when you need check whether the user gives you the right number of arguments
  • Checking for the correct number of arguments should be the first check in any script that expects input arguments
    • You can check [ $# -ne 3 ] if you want an exact number of arguments, for example, exactly 3
    • You can check [ $# -gt 2 ] if you are expecting a variable number of arguments, for example, has to be larger than 2

Command Line Variables 1 - 9

  • Each command line argument is stored in its own variable, with the names 1, 2, 3, …, 8, 9. The first argument goes into variable 1, the second argument goes into variable 2, etc.
  • These variables are accessed with $1, $2, …, $8, $9, just like any other variable names
  • The 1 – 9 variables are also called positional parameters or variables because their names indicate the positions of their argument on the command line
  • For argument counts that are larger than 9, { } are needed when accessing the variables. For example, ${14} is used to access the 14th argument on the command line
  • For example, the script:
#!/bin/bash

echo the script $0 has $# argument

echo first: $1, second: $2, third: $3

when run on the command line as: arg.sh aaa bbb ccc

will print:

the script arg.sh has 3 arguments

first: aaa, second: bbb, third: ccc

Command Line Variables * and @

  • In addition to being accessed through its own variable, the command line arguments can be accessed through the variables * and @
  • Both * and @ stores the list of all command line arguments
  • For example, the scripts:

for i in $* for i in $@

do do

echo $i echo $i

done done

    • will both print all command line arguments, one per line
  • The only difference between @ and * is when they are accessed in double quotes.
  • “$*” is equivalent to: “$1 $2 $3 $4 …”
    • * becomes a string with the list of command line arguments concatenated together
  • “$@” is equivalent to “$1”, “$2”, “$3”, “$4”, …
    • @ becomes the array of individual command line arguments


  • An example showing the difference between $@ and $* is as follows
  • Consider the script:

for i in “$*”

do

echo $i

done

  • will print one line of all command line arguments, and the loop runs once because “$*” is a list of one element


  • But the script:

for i in “$@”

do

echo $i

done

  • will print all command line arguments, one per line, and the loop runs as many times as there are number of arguments

set

  • The set command will take each of its arguments and store them in the positional variables
  • For example:

$ set one two three

$ echo $*

one two three

$ echo $3

three

  • set can be used to conveniently parse command output into separate variables

$ set `date`

$ echo $2

Apr

$ echo $5

PDT

$ echo $*

Sat Apr 16 19:09:09 PDT 2011


shift

  • The shift utility will shift left each of the positional variables by a certain number of positions
  • The default number of positions is 1
  • The value shifted out of the variable 1 is gone
  • For example:

$ set one two three four

$ echo $*

one two three four

$ shift 2

$ echo $*

three four one and two are gone

$ shift

$ echo $2

there is nothing in 2

$ echo $1

four

  • The shift command is often used in a loop to process each of the positional variables one by one

Command Line Options

  • Recall that each command can accept options as well as arguments
  • Therefore, the variables for command line arguments could be used for accessing options as well as arguments on the command line
  • For example, in the command line: myScript –a –b c d
    • the options are –a and –b, the arguments are c and d,
    • and $1 = -a $2 = -b $3 = c $4 = d
  • Using the positional variables becomes a problem when the options are grouped together, such as:
    • myScript –ab c d where $1 = -ab $2 = c $3 = d
      • It takes extra work to separate the 2 options a and b
  • Instead, we can use the getopts command

getopts

  • getopts performs 1 of 2 tasks with options on the command line:
    • If the option is valid, read the option into a variable
    • If the option is not valid, print an error message
  • Format: getopts option_list variable_name
    • option_list are characters that are acceptable for an option
    • variable_name is where a matching option will be stored
  • If an option on the command line matches a character in the option_list, then it is a valid option. If it does not match any character in option_list, then it is invalid
  • In a script, each time that getopts runs, it goes through the options on the command line one by one.
  • As long as there are options, getopts returns true, and when there are no more options, getopts returns false
  • Therefore getopts are usually run in a loop to validate and analyze options until getopts returns false.
  • In each iteration of the loop, the option is analyzed and the appropriate actions are done for the option
Example:

while getopts ab var

do

     case $var in
       a) echo option is a
       ;;
       b) echo option is b
     esac
   done
  • Since getopts returns true or false, it can be used in a while loop to step through all the options
  • There is no [ ] needed for the while condition because there is no operator evaluation here. getopts itself returns true or false
  • In each iteration of the loop, the current option in var is evaluated and some appropriate action for the option is taken. In this example, the action is just to echo the option.
  • The loop ends when there is no more option and getopts returns false
  • If an option has an argument, you can tell getopts to read in the argument for that option by adding a : after the option character in the option_list
  • Example: getopts abc:d: var
    • The valid options are a, b, c and d
    • Options c and d can accept an argument, which should come right after the option on the command line
    • getopts will store the argument in the variable OPTARG
  • Combining options and arguments on the command line:
    • myscript –ad fileA fileB fileC
      • a is accessed with $var the first time getopts runs
      • d is accessed with $var and fileA is accessed with $OPTARG the second time getopts runs
      • -ad is accessed with $1
      • fileA is accessed with $2
      • fileB is accessed with $3
      • fileC is accessed with $4