[Linux Shell学习系列十]脚本输入处理-2选项处理
D18
通过位置参数调用命令行参数,就要求调用脚本时输入的多个参数顺序必须是固定的。可能引起因输入顺序错误而产生异常的情况。因此为了避免这个情况,也使脚本更加严谨,当编写一个功能复杂的脚本时,我们通常让脚本具有可以指定选项的功能。
像Linux下的许多命令行工具一样,在调用脚本时,可以在命令后面指定不同的选项及命令行参数。
1. 使用case语句处理命令行选项
Shell脚本只接收一个命令行选项时,使用case语句进行处理。
$ cat processFile.sh #!/bin/bash #202005 opt=$1 filename=$2 checkfile() { if [ -z $filename ] then echo "File name missing" exit 1 elif [ ! -f $filename ] then echo "The file $filename does not exist" exit 2 fi } case $opt in -e|-E ) checkfile echo "Editing $filename file..." ;; -p|-P ) checkfile echo "Displaying $filename file..." ;; * ) echo "Bad argument!" echo "Usage: `basename $0` -e|-p filename" exit 3 ;; esac $ ./processFile.sh Bad argument! Usage: processFile.sh -e|-p filename $ ./processFile.sh -p File name missing $ ./processFile.sh -p list.txt Displaying list.txt file... $ ./processFile.sh -E list.txt Editing list.txt file...
2. 使用getopts处理多命令行选项
D19
getopts是Bash的内部命令,可以以专业的方式解析命令行选项和参数。
它的优势在于:
1)不需要通过外部程序来处理位置参数;
2)getopts可以很容易地设置你可以用解析的Shell变量(对一个外部进程是不可能的);
3)getopts定义在POSIX中。
注意:getopts不能解析GNU风格的长选项(--myoption)或XF86风格的长选项(-myoptions)!
用法:
1)getopts会识别所有这些选项格式,指定的选项可以是大写字母或小写字母或数字。(虽然也能识别其他字符,但不推荐使用)
2)通常需要多次调用getopts。因为它本身不会更改位置参数的设置,如果想将位置参数移位,必须使用shift来处理。
3)当没有内容可解析时,getopts会设置一个退出状态FALSE,很容易在while中使用。
while getopts ...; do ... done
4) getopts将解析选项和他们可能的参数,到第一个非选项参数(不以-k开头的,且不是它前面的任何选项的参数的字符串)的位置停止解析。当遇到双连字符--(表示选项的结束)时,它也将停止解析。
getopts会使用以下3个变量:
1)OPTIND:存放下一个要处理的参数的索引。getopts通过它记住自己的状态,同样可以用于移位使用getopts处理后的位置参数。OPTIND初始被设为1,并且如果你想再次使用getopts解析任何内容,需要将其重置为1。
2)OPTARG:被设置为由getopts找到的选项所对应的参数。
3)OPTERR:取值为0或1,指示Bash是否应该显示由getopts产生的错误信息。当每个Shell启动时,它的值被初始化为1,如果你不想看到烦人的信息,请设置为0。
4)基本语法:
getopts OPTSTRING VARNAME [ARGS...]
OPTSTRING:告诉getopts会有哪些选项和在哪会有参数(选项后加冒号:表示,见下面的示例);
VARNAME:告诉getopts哪个变量用于选项报告;
ARGS:告诉getsopts解析这些可选的参数,而不是位置参数。默认情况下,getopts解析当前Shell或函数的位置参数。当额外指定了VARNAME后,getopts将不再尝试解析位置参数,而是解析这些指定的参数。
5)getopts支持两种错误报告的模式:详细和抑制。产品中的脚本推荐使用抑制错误报告模式,看起来更专业,没有恼人的标准信息,也更容易处理,因为以更简单的方法显示了失败的情况。
| 场景 | 报告模式 | VARNAME | OPTARG | 其他 |
| 遇到一个无效的选项 | 详细 | 被设置为? | 不会被设置 | |
| 抑制 | 被设置为? | 被设置为选项字符 | ||
| 需要的参数没有找到 | 详细 | 被设置为? | 不会被设置 | 打印错误信息 |
| 抑制 | 被设置为:(这里有疑问) | 包含选项字符 |
#例1:单个选项脚本
$ cat getopts_1.sh #!/bin/bash #202005 while getopts ":a" opt do case $opt in a) echo "The option -a was triggered!" ;; \?) echo "Invalid option: -${OPTARG}" ;; esac done $ ./getopts_1.sh #不带选项时无输出,因为getopts没有看到任何有效的或无效的选项 $ ./getopts_1.sh list.txt #指定参数但不是选项,也没有输出 $ ./getopts_1.sh -b #指定一个错误的选项 Invalid option: -b $ ./getopts_1.sh -a #指定选项-a The option -a was triggered! $ ./getopts_1.sh -a -b -x -d #指定多个选项,会逐一处理 The option -a was triggered! Invalid option: -b Invalid option: -x Invalid option: -d $ ./getopts_1.sh -a -a -a -a #指定多个相同的选项,也会逐一处理 The option -a was triggered! The option -a was triggered! The option -a was triggered! The option -a was triggered! $ ./getopts_1.sh -aaaa #指定多个选项,会逐一处理 The option -a was triggered! The option -a was triggered! The option -a was triggered! The option -a was triggered!
引起两点考虑:
1)无效的选项不会停止处理:如果要停止运行,必须自己完善操作:在正确的位置执行exit命令;
2)多个相同的选项是可能的:如果想禁止重复的选项,必须在脚本中做一些检查操作。
#例2:多个选项的脚本 $ cat getopts_2.sh #!/bin/bash #202005 vflag=off filename="" output="" function usage() { echo "USAGE:" echo " myscript [-h] [-v] [-f <filename>] [-o <filename>]" exit -1 } while getopts :hvf:o: opt do case "$opt" in v) vflag=on ;; f) filename=$OPTARG if [ ! -f $filename ] then echo "The source file $filename doesn't exist!" exit fi ;; o) output=$OPTARG if [ ! -d `dirname $output` ] then echo "The output path `dirname $output` doesn't exist!" exit fi ;; h) usage exit ;; :) echo "The option -$OPTARG requires an argument." exit 1 ;; ?) echo "Invalid option: -$OPTARG" usage exit 2 ;; esac done $ ./getopts_2.sh #不输入选项 $ ./getopts_2.sh -h #-h选项 USAGE: myscript [-h] [-v] [-f <filename>] [-o <filename>] $ ./getopts_2.sh -vf #-f选项需要指定参数 The option -f requires an argument. $ ./getopts_2.sh -vf list.txt2 #-f选项输入的filename不存在 The source file list.txt2 doesn't exist! $ ./getopts_2.sh -vf list.txt -o test #正确
3. 使用getopt处理多命令行选项
getopt命令与getopts命令相似,也是用于解析命令行的选项和参数。不同的是,getopt是Linux下的命令行工具,并且getopt支持命令行的长选项(--some-option)。另外,在脚本中,它们的调用方式也不同。
语法:
getopt [options] [--] optstring parameters getopt [options] -o|--options optstring [options] [--] parameters
使用getopt:
$ getopt f:vl -vl -f/dir/filename param_1 #f:vl对应语法中的optstring,-vl-f/dir/filename对应parameters -v -l -f /dir/filename -- param_1
getopt会按照optstring的设置,将parameters解析为相应的选项和参数:
-vl被解析为-v和-l;-f/dir/filename被解析为-f /dir/filename;然后解析后的命令行选项和参数之间用双连字符--分隔。
例1:使用getopt命令重写命令行参数:
$ cat getopt_1.sh #!/bin/bash #202005 set -- `getopt f:vl "$@"` #getopt f:vl "$@"表示将传递给脚本的命令行选项和参数作为getopt命令的参数,由getopt命令解析处理 #整个set命令则表示将getopt命令的输出作为值依次从左到右赋值给未知参数 while [ $# -gt 0 ] do echo $1 shift done $ ./getopt_1.sh -vl -f/dir/filename param_1 -v -l -f /dir/filename -- param_1
例2:将使用getopts的脚本改写为使用getopt命令
$ cat getopt_2.sh #!/bin/bash #202005 vflag=off filename="" output="" #使用getopt命令处理后的命令行选项和参数,重新设置位置参数 set -- `getopt hvf:o: "$@"` function usage() { echo "USAGE:" echo " myscript [-h] [-v] [-f <filename>] [-o <filename>]" exit -1 } #位置参数的个数大于0时循环 while [ $# -gt 0 ] do case "$1" in -v) vflag=on ;; -f) filename=$2 if [ ! -f $filename ] #如果-f选项的参数(文件)不存在,输出并退出脚本运行;否则将位置参数左移,移到-f选项的参数的位置 then echo "The source file $filename doesn't exist!" exit else shift fi ;; -o) output=$2 if [ ! -d `dirname $output` ] #如果作为-d选项的参数(目录)不存在,则显示信息并退出脚本运行;否则将位置参数左移,移到-o选项的参数的位置 then echo "The output path `dirname $output` doesn't exist!" exit else shift fi ;; -h) usage exit ;; --) shift #如果已经移到双连字符,则跳过并退出循环 break ;; -*) echo "Invalid option: -$1" #不合法的选项 usage exit 2 ;; *) break #没有选项 ;; esac shift done
执行上面的代码,与原来的getopts_2.sh一致:
$ ./getopt_2.sh -h USAGE: myscript [-h] [-v] [-f <filename>] [-o <filename>] $ ./getopt_2.sh -vf getopt: option requires an argument -- 'f' $ ./getopt_2.sh -vf list.txt $ ./getopt_2.sh -vf list.txt -o getopt: option requires an argument -- 'o' $ ./getopt_2.sh -vf list.txt -o test
例3:支持长选项
$ cat getopt_longopt.sh #!/bin/bash #202005 ARG_B=0 #eval命令用于将气候的内容作为单个命令读取和执行,这里用于处理getopt命令生成的参数的转义字符 #set --命令,将getopt处理脚本命令行选项和参数的结果重新赋值给位置参数 eval set -- `getopt -o a::bc: --long arga::,argb,argc: -n 'getopt_longopt.sh' -- "$@"` #getopt -o a::bc: #表示:-o后面的每个字符表示一个选项;::前的选项a有一个可选参数;:前的选项c必须有一个参数;b选项没有参数; #getopt --long arga::,argb,argc:
#表示:--long后面的长选项用逗号,分割;::前的选项有arga一个可选参数;:前的选项argc必须有一个参数;argb选项没有参数 while true #循环 do case "$1" in -a|--arga) case "$2" in "") ARG_A='default value' #没有给出-a或--arga选项(可选参数)的参数时,使用默认值赋值给ARG_A shift ;; *) ARG_A=$2 #给出-a或--arga选项(可选参数)的参数时,使用参数值赋值给ARG_A shift ;; esac ;; -b|--argb) ARG_B=1 #指定了-b或--argb选项,ARG_B赋值1 ;; -c|--argc) case "$2" in "") shift #没有指定-c或--argc选项的参数时,跳过 ;; *) ARG_C=$2 #给出-c或--argc选项参数时,使用参数值赋值给ARG_C shift break ;; esac ;; --) shift #双连字符--,则退出while循环,表示选项处理结束 break ;; *) echo "Internal error!" #其他选项,报错并退出脚本执行 exit 1 ;; esac shift done #打印变量值 echo "ARTG_A= $ARG_A" echo "ARTG_B= $ARG_B" echo "ARTG_C= $ARG_C"
-o选项指定的选项需注意:
1)具有可选参数的选项-a,指定参数时,选项a与参数之间不能有任何空格,即-aparama;
2)必须具有参数的-c选项,指定参数时,可以是-c paramc。
$ ./getopt_longopt.sh ARTG_A= #没有指定-a选项,因此没有赋值 ARTG_B= 0 #没有指定-b选项,ARTG_B为初始值 ARTG_C= #没有指定-a选项,因此没有赋值 $ ./getopt_longopt.sh -a -b -c 456 ARTG_A= default value #指定-a选项但没有变量,赋默认值 ARTG_B= 1 #指定-b选项,执行赋值语句赋值为1 ARTG_C= 456 #指定-c选项和变量456,赋值456 $ ./getopt_longopt.sh -a123 -b -c 456 ARTG_A= 123 #指定-a选项和变量123,赋值123 ARTG_B= 1 #指定-b选项,执行赋值语句赋值为1 ARTG_C= 456 #指定-c选项和变量456,赋值456 $ ./getopt_longopt.sh -c 456 ARTG_A= #没有指定-a选项,因此没有赋值 ARTG_B= 0 #没有指定-b选项,ARTG_B为初始值 ARTG_C= 456 #指定-c选项和变量456,赋值456
--long指定的长选项需注意:
1)具有可选参数的长选项--arga,指定参数时,选项a与参数之间只能用等号=连接,即--arga=parama;
2)必须具有参数的--argc选项,指定参数时,可以是--argc paramc或--argc=paramc。
$ ./getopt_longopt.sh --arga --argb --argc 456 ARTG_A= default value ARTG_B= 1 ARTG_C= 456 $ ./getopt_longopt.sh --arga=123 --argb --argc 456 ARTG_A= 123 #采用=连接--arga及其参数 ARTG_B= 1 ARTG_C= 456 #采用空格连接--argc及其参数 $ ./getopt_longopt.sh --argc=456 ARTG_A= ARTG_B= 0 ARTG_C= 456 #采用=连接--argc及其参数 $ ./getopt_longopt.sh --arga 123 ARTG_A= default value #没有采用=所以123没有作为--arga选项的参数 ARTG_B= 0 ARTG_C=
根据需求选择getopts或getopt:
1)如果需要支持长选项,选getopt;
2)如果需要考虑跨平台的兼容性,选getopts,兼容性较好,使用更简单。
本节结束

浙公网安备 33010602011771号