for|case|select|while and until
Command-Line Options and Typed Variables
Command-Line Options
Typical UNIX commands have the form command [-options]args, meaning that there can be 0 or more options. If a shell script processes the command teatime alice hatter, then $1 is “alice” and $2 is “hatter”. But if the command is teatime -o alice hatter, then $1 is -o, $2 is “alice”, and $3 is “hatter”.
You might think you could write code like this to handle it:
if [ $1 = -o ]; then code that processes the -o option 1=$2 2=$3 fi normal processing of $1 and $2...
But this code has several problems. First, assignments like 1=$2 are illegal because positional parameters are read-only. Even if they were legal, another problem is that this kind of code imposes limitations on how many arguments the script can handle—which is very unwise. Furthermore, if this command had several possible options, the code to handle all of them would get very messy very quickly.
shift
The getopts built-in command, which we will introduce later, provides this help.
if [ -n "$(echo $1 | grep '^-[0-9][0-9]*$')" ]; then howmany=$1 shift elif [ -n "$(echo $1 | grep '^-')" ]; then print 'usage: highest [-N] filename' exit 1 else howmany="-10" fi filename=$1 sort -nr $filename | head $howmany
This uses the grep search utility to test if $1 matches the appropriate pattern. To do this we provide the regular expression ^-[0-9][0-9]*$ to grep, which is interpreted as “an initial dash followed by a digit, optionally followed by one or more digits.” If a match is found then grep will return the match and the test will be true, otherwise grep will return nothing and processing will pass to the elif test. Notice that we have enclosed the regular expression in single quotes to stop the shell from interpreting the $ and *, and pass them through to grep unmodified.
For the sake of concreteness, assume that our script is called alice and we want to handle the options -a, -b, and -c:
while [ -n "$(echo $1 | grep '-')" ]; do case $1 in -a ) process option -a ;; -b ) process option -b ;; -c ) process option -c ;; * ) echo 'usage: alice [-a] [-b] [-c] args...' exit 1 esac shift
done normal processing of arguments...
#shift在干什么?let's see
cor@debian:~/shell/mar5$ cat 2.sh echo "==================" echo ${#*} #查看当前position 参数的个数 echo 'position 1 = '${1:-None} #如果有就返回,否则返回None echo 'position 2 = '${2:-None} echo 'position 3 = '${3:-None} echo "========1========" shift echo ${#*} echo 'position 1 = '${1:-None} echo 'position 2 = '${2:-None} echo 'position 3 = '${3:-None} shift echo "========2========" echo 'position 1 = '${1:-None} echo 'position 2 = '${2:-None} echo 'position 3 = '${3:-None} shift echo ${#*} echo "========3========" echo 'position 1 = '${1:-None} echo 'position 2 = '${2:-None} echo 'position 3 = '${3:-None}
result:
cor@debian:~/shell/mar5$ . 2.sh a b c ================== 3 #第一次的总数是3 position 1 = a position 2 = b position 3 = c ========1======== 2 # shift 后原来的 $1 被remove 掉了,后面的往前顶 position 1 = b position 2 = c position 3 = None #此时,position 3 的返回值是None ========2======== position 1 = c position 2 = None position 3 = None 0 ========3======== position 1 = None position 2 = None position 3 = None
Options with Arguments
Assume that, in our alice script, the option -b requires its own argument. Here is the modified code that will process it:
while [ -n "$(echo $1 | grep '-')" ]; do case $1 in -a ) process option -a ;; -b ) process option -b $2 is the option's argument shift ;; -c ) process option -c ;; * ) echo 'usage: alice [-a] [-b barg] [-c] args...' exit 1 esac shift done normal processing of arguments...
getopts
So far, we have a complete, but constrained, way of handling command-line options.The above code does not allow a user to combine arguments with a single dash, e.g., -abc instead of -a -b -c. It also doesn’t allow one to specify arguments to options without a space in between, e.g., -barg in addition to -b arg.
getopts takes two arguments. The first is a string that can contain letters and colons.Each letter is a valid option; if a letter is followed by a colon, the option requires an argument. getopts picks options off the command line and assigns each one (without the leading dash) to a variable whose name is getopts’s second argument. As long as there are options left to process, getopts will return exit status 0; when the options are exhausted, it returns exit status 1, causing the while loop to exit.
while getopts ":ab:c" opt; do case $opt in a ) process option -a ;; b ) process option -b $OPTARG is the option's argument ;; c ) process option -c ;; ? ) echo 'usage: alice [-a] [-b barg] [-c] args...' exit 1 esac done shift $(($OPTIND - 1)) normal processing of arguments...