[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,兼容性较好,使用更简单。

 

本节结束

posted @ 2020-05-28 15:32  workingdiary  阅读(616)  评论(0)    收藏  举报