脚本输入处理——选项处理

脚本输入处理

选项处理

如果一个脚本有多个命令行参数,调用这个脚本时所指定的命令行参数顺序不固定,那么即是参数的个数正确,脚本也不一定可以正常运行。如:

一个脚本(process.sh),可以接受三个参数:配置文件、输入数据的文件和脚本的数据文件(default.conf、input.txt、output.txt)。此脚本读取配置文件内容,处理input.txt文件,然后将输出写入到output.txt中。那么参数的顺序就由为重要了。因为:

bash process.sh default.conf input.txt output.txt

bash process.sh input.txt output.txt default.conf

完全是不同的。

为了避免上述的情况,和使得脚本更加严谨,当编写一个功能比较复杂的脚本时,通常要使得脚本具有可以指定选项的功能。如:对于上述的脚本文件process.sh,可以使用"-c"选项表示指定配置文件,"-o"选项指定输出文件。

使用case语句处理命令行选项

如果只接受一个命令行选项时,可以使用case语句进行处理。

如:

[root@rs1 case]# cat process.sh 
#!/bin/bash#将第一个命令行参数赋值给变量opt
opt=$1
#将第二个命令行参数赋值给filename
filename=$2#定义函数checkfile
checkfile() {    #如果没有$2,则提示缺少文件,并退出
    if test -z $filename
    then
        #显示缺少参数$2
        echo "FIle name missing."
        #退出,返回状态码为1
        exit 1
    elif test ! -f $filename
    then
        #如果文件不存在,显示文件不存在
        echo "The file $filename does not exist!"
        #退出,返回状态码2
        exit 2
    fi      
}
​
case $opt in 
    #匹配-e或者-E选项
    -e|-E)
        #调用checkfile函数
        checkfile
        echo "Editing $filename file..."
        #运行编辑文件的命令、函数或脚本
        #Running command or function to edit the file.
        echo "I Like You But Just Like You" >> $filename
        ;;
    #匹配-p或者-P选项
    -p|-P)
        #调用checkfile函数
        checkfile
        echo "Displaying $filename file..."
        #运行显示文件的命令、函数或脚本
        cat $filename
        ;;
​
        #匹配其他选项
    *)
        echo "Bad arguement!"
        echo "Usage `basename $0` -e|-p filename"
        echo "  -e filename: Edit file."
        echo "  -p filename: Display file."
        ;;
esac

执行脚本:

[root@rs1 case]# bash process.sh    //不加参数,提示参数错误
Bad arguement!
Usage process.sh -e|-p filename
    -e filename: Edit file.
    -p filename: Display file.
[root@rs1 case]# bash process.sh -e //只添加选项,提示文件名丢失,即参数不完整
FIle name missing.
[root@rs1 case]# bash process.sh -e 2.txt   //添加参数完整,但是2.txt不存在
The file 2.txt does not exist!
[root@rs1 case]# touch 2.txt        //创建2.txt
[root@rs1 case]# bash process.sh -e 2.txt   //正确运行,edit file
Editing 2.txt file...
[root@rs1 case]# bash process.sh -p 2.txt   //查看文件
Displaying 2.txt file...
I Like You But Just Like You

使用getopt处理多个命令行选项

在使用命令时,我们可以进行选项合并:比如,使用命令ls -l -a -i时,可以写成ls -ali。这就要借助于getopts(或者getopt)来处理多命令行选项。

getopts是一个更专业的处理命令行选项和参数的工具,是Bash的内部命令,优势有三:

  1. 不需要额外的再通过一个外部程序处理位置参数

  2. getopts可以更容易的解析Shell变量

  3. getopts定义在POSIX中

getopt不能解析长选项

如:

只有一个选项

第一个脚本只有一个-a选项,没有任何参数

[root@rs1 getopts]# cat getopts.sh 
#!/bin/bash#使用getopts解析命令行选项,这里只有一个-a选性,选项字符串中的第一个字符为:(冒号),表示抑制错误报告
while getopts ":a" opt
do
    case $opt in
    #匹配-a选项,注意这里case条件是a不是-a
        a)
            echo "Tht option -a was triggered."
            ;;
        \?)
            echo "Invalid option: -${OPTARG}"
            ;;
    esac
done
  • 当不指定任何选项时

    [root@rs1 getopts]# bash getopts.sh 
    [root@rs1 getopts]# 
  • 当没有指定选项,而是制定了一个命令行参数时

    [root@rs1 getopts]# bash getopts.sh a
    [root@rs1 getopts]# 
  • 当指定了错误的选项时

    [root@rs1 getopts]# bash getopts.sh -b
    Invalid option: -b
  • 当指定了正确的选项时

    [root@rs1 getopts]# bash getopts.sh -a
    Tht option -a was triggered.
  • 当指定了多个选项,有对有错时

    [root@rs1 getopts]# bash getopts.sh -a -b -c -f -ab
    Tht option -a was triggered.
    Invalid option: -b
    Invalid option: -c
    Invalid option: -f
    Tht option -a was triggered.
    Invalid option: -b

总结:

  1. getopts没有看到任何有效的或者无效的选项,就不会被触发

  2. 如果运行时指定了一个命令行参数,而不是选项,同样不会被触发

  3. 当指定了一个错误的(没有被getopts解析)的选项时,getopts被出发了,但是没有接收这个选项,而是将\?放入变量opt中,并将无效的选项字符放入变量OPTARG中(这个变量被设置为由getopts找到的选项所对应的参数)

  4. 重复指定同一个选项多次,getopts就会被触发多次

所以需要注意:

  • 无效的选项不会停止处理;如果希望脚本在接受到无效的选项时停止运行,就需要做一些完善工作。如在正确位置执行exit命令

  • 多个相同的选项无意义出现时,应该禁止,所以还需要添加一些检查操作

多个选项和参数
[root@rs1 getopts]# cat getopts_2.sh 
#!/bin/bash#定义变量vflag
vflag=off
#定义变量filename
filename=""
#定义变量output
output=""#定义函数usage
usage() {    echo "Usage:"
    echo " myscript [-h] [-v] [-f <filename>] [-o <filename>]"
    exit -1
}
​
#在while循环中使用getopts解析命令行选项
#要解析的选项有-h、-v、-f和-o,其中-f和-o选项带有参数
#字符串选项中第一个冒号表示getopts使用抑制错误报告模式
while getopts :hvf:o: opt
do
    case "$opt" in
        v)
            vflag=on
            echo "\$vflag : $vflag"
            ;;
        f)
            #将-f选项的参数赋值给变量filename
            filename=$OPTARG
            #如果文件不存在,则显示提示信息,并退出脚本
            if test ! -f $filename
            then
                echo "The source file $filename does not exist!"
                exit 1
            else
                echo "For -f `date`  I Like You But Just Like You." >> $filename
            fi
            ;;
        o)
            #将-o选项的参数赋值给变量output
            output=$OPTARG
            #如果指定的输出文件不存在,就创建一个文件
            if test ! -f $output
            then
                touch $output
            fi
                echo "For -o `date`  I Like You But Just Like You." >> $output
                cat $output
            ;;
        h)
            #显示脚本的使用信息
            usage
            exit 0
            ;;
        :)
            #如果没有为需要参数的选项指定参数,则显示提示信息,并退出脚本运行
            echo "The Option -$OPTARG requires an argument."
            exit 1
            ;;
        ?)
            #如果指定的选项为无效选项,则显示提示信息,及脚本使用方法,并退出脚本运行
            echo "Invalid option: -$OPTARG"
            usage
            exit 2
            ;;
        esac
done
  • 当执行-h、-h选项时

    [root@rs1 getopts]# bash getopts_2.sh -h
    Usage:
     myscript [-h] [-v] [-f <filename>] [-o <filename>]
    [root@rs1 getopts]# bash getopts_2.sh -v
    $vflag : on
    [root@rs1 getopts]# bash getopts_2.sh -vh
    $vflag : on
    Usage:
     myscript [-h] [-v] [-f <filename>] [-o <filename>]
  • 单添加-f、-o选项时

    • 对于-f选项

      [root@rs1 getopts]# bash getopts_2.sh -f 1.txt
      The source file 1.txt does not exist!
      [root@rs1 getopts]# touch 1.txt
      [root@rs1 getopts]# bash getopts_2.sh -f 1.txt
      [root@rs1 getopts]# cat 1.txt 
      For -f Mon Jul  9 02:53:04 CST 2018  I Like You But Just Like You.
    • 对于-o选项

      [root@rs1 getopts]# bash getopts_2.sh -o 4.txt
      For -o Mon Jul  9 02:53:34 CST 2018  I Like You But Just Like You.
    • 当-f、-o同时使用时

      [root@rs1 getopts]# bash getopts_2.sh -o 3.txt -f 3.txt
      For -o Mon Jul  9 02:53:53 CST 2018  I Like You But Just Like You.
      ​
      [root@rs1 getopts]# cat 3.txt 
      For -o Mon Jul  9 02:53:53 CST 2018  I Like You But Just Like You.
      For -f Mon Jul  9 02:53:53 CST 2018  I Like You But Just Like You.
    • 对于错误选项

      [root@rs1 getopts]# bash getopts_2.sh -x
      Invalid option: -x
      Usage:
       myscript [-h] [-v] [-f <filename>] [-o <filename>]

使用getopt处理多命令行选项

getopt和getopts很像,也是用来解析命令行选项和参数,使其可以被Shell程序简单的解析。不同指出在于:getopt命令时Linux下的命令行工具,支持长选项。在脚本中的调用方式不同。

如:

第一个实例
[root@rs1 getopts]# cat getopt_1.sh 
#!/bin/bash#将getopt命令解析后的内容设置到位置参数
set -- `getopt f:vl "$@"`
​
​
#如果位置参数的个数大于0,就执行while循环
while [ $# -gt 0 ]
do
    #打印$1的值
    echo $1
    #将位置参数左移
    shift
done

执行脚本:

[root@rs1 getopts]# bash getopt_1.sh -vl -f1.txt param_1
-v
-l
-f
1.txt
--
param_1

解释:getopt f:vl "$@"

"f:vl"中,vl被解析成"-v"和"-l",因为"-f"后面有冒号,所以-f1.txt被解析成"-f 1.txt"。然后解析后的命令行选项和参数之间使用双连字符(--)分割。

set命令中," getopt f:vl "$@" "表示将传递给脚本getopt_1.sh的命令行选项和参数作为getopt命令的参数,由getopt命令解析处理。整个set命令语句"set -- `getopt f:vl "$"`"表示将getopt命令的输出作为值一次赋值给位置参数。

第二个实例
[root@rs1 getopts]# cat getopt_2.sh 
#!/bin/bash#定义变量vflag
vflag=off
#定义变量filename
filename=""
#定义变量output
output=""#使用getopt命令处理后的命令行选项和参数重新设置位置参数
set -- `getopt hvf:o: "$@"`#定义函数usage
usage() {    echo "Usage:"
    echo " myscript [-h] [-v] [-f <filename>] [-o <filename>]"
    exit -1
}
​
#在while循环中使用getopts解析命令行选项
#要解析的选项有-h、-v、-f和-o,其中-f和-o选项带有参数
while [ $# -gt 0  ]
do
    case "$1" in
        -v)
            vflag=on
            echo "\$vflag : $vflag"
            ;;
        -f)
            #将-f选项的参数赋值给变量filename
            filename=$2
            #如果指定的作为参数的文件不存在,则显示文件不存在到标准输出,并退出脚本运行
            #否则将位置左移,移到-f选项的参数位置
            if test ! -f $filename
            then
                echo "The source file $filename does not exist!"
                exit 1
            else
                shift
                echo "For -f `date`  I Like You But Just Like You." >> $filename
            fi
            ;;
        -o)
            #将-o选项的参数赋值给便来嗯filename
            output=$2
            #如果指定的输出文件不存在,则显示文件不存在到标准输出,并退出脚本运行
            #否则将位置参数左移,移动到-o选项的参数的位置
            if test ! -d `dirname $output`
            then
                echo "The output path `dirname $output` does not exist!"
                exit
            else
                shift
                echo "For -o `date`  I Like You But Just Like You." >> $output
                cat $output
            fi
            ;;
        --)
            #如果是--,则跳过,并退出while循环
            shift
            break
            ;;
        -*)
            echo "Invalid option: $1"
            usage
            exit 2
            ;;
        *)
            break
            ;;
        esac
        shift
done

执行脚本:

[root@rs1 getopts]# bash getopt_2.sh -h     //使用-h选项
Usage:
 myscript [-h] [-v] [-f <filename>] [-o <filename>]
 
[root@rs1 getopts]# bash getopt_2.sh -vf    //使用-vf,被解析成-v -f,-v不需要参数,可以执行。-f需要参数,所以提示getopt: option requires an argument -- 'f'
getopt: option requires an argument -- 'f'
$vflag : on
[root@rs1 getopts]# bash getopt_2.sh -f 1.txt //选项和参数都正确,执行成功
[root@rs1 getopts]# cat 1.txt 
For -f Mon Jul  9 02:53:04 CST 2018  I Like You But Just Like You.
For -f Mon Jul  9 03:53:08 CST 2018  I Like You But Just Like You.  //日期是和电脑时间同步,刚执行结束的
[root@rs1 getopts]# date
Mon Jul  9 03:53:16 CST 2018
​
[root@rs1 getopts]# bash getopt_2.sh -f 1.txt -o    //-o也需要参数,所以提示缺少参数
getopt: option requires an argument -- 'o'
​
[root@rs1 getopts]# bash getopt_2.sh -f 1.txt -o /mnt/
getopt_2.sh: line 55: /mnt/: Is a directory
cat: /mnt/: Is a directory
​
[root@rs1 getopts]# bash getopt_2.sh -f 1.txt -o 2.txt //执行成功,-o参数为2.txt
Mon Jul  9 02:48:46 CST 2018  I Like You But Just Like You.
For -o Mon Jul  9 03:35:58 CST 2018  I Like You But Just Like You.
For -o Mon Jul  9 03:37:56 CST 2018  I Like You But Just Like You.
For -o Mon Jul  9 03:38:03 CST 2018  I Like You But Just Like You.
For -o Mon Jul  9 03:53:48 CST 2018  I Like You But Just Like You.  //最新的结果,因为电脑此时时间为下:
[root@rs1 getopts]# date
Mon Jul  9 03:53:53 CST 2018

执行一次全选项,分析过程:

[root@rs1 getopts]# bash -x getopt_2.sh -vf /etc/passwd -o 2.txt 
+ vflag=off
+ filename=
+ output=
++ getopt hvf:o: -vf /etc/passwd -o 2.txt
+ set -- -v -f /etc/passwd -o 2.txt -- //六个变量 -v -f /etc/passwd -o 2.txt --。这是getopt进行解析的过程,十分重要
+ '[' 6 -gt 0 ']'
+ case "$1" in      //此时,-v为$1
+ vflag=on
+ echo '$vflag : on'
$vflag : on
+ shift
+ '[' 5 -gt 0 ']'
+ case "$1" in          //此时,-f为$1,$2为"/etc/passwd"字符串
+ filename=/etc/passwd      //这一步将-f选项的参数$2赋值给变量filename
+ test '!' -f /etc/passwd   //不为空
+ shift     //-f选项被丢掉,$1变成"/etc/passwd"
++ date
+ echo 'For -f Mon Jul  9 03:38:03 CST 2018  I Like You But Just Like You.'
+ shift     //执行了最后的shift,丢掉了"/etc/passwd",此是$1时-o
+ '[' 3 -gt 0 ']'   //此时,$1是-o
+ case "$1" in
+ output=2.txt      //将-o的参数,2.txt赋值给output变量
++ dirname 2.txt    //执行命令
+ test '!' -d .     //测试2.txt时不是目录文件,经过测试不是
+ shift         //此时$1变成2.txt
++ date
+ echo 'For -o Mon Jul  9 03:38:03 CST 2018  I Like You But Just Like You.'
+ cat 2.txt
Mon Jul  9 02:48:46 CST 2018  I Like You But Just Like You.
For -o Mon Jul  9 03:35:58 CST 2018  I Like You But Just Like You.
For -o Mon Jul  9 03:37:56 CST 2018  I Like You But Just Like You.
For -o Mon Jul  9 03:38:03 CST 2018  I Like You But Just Like You.
+ shift     //此是$1变成--
+ '[' 1 -gt 0 ']'   //因为$1仍然有值
+ case "$1" in      //,$1的值是--,所以准备跳出while循环
+ shift     //执行esac后面的shirt
+ break     //跳出脚本运行
getopt支持长选项
[root@rs1 getopts]# cat getopt_3.sh 
#!/bin/bash#定义变量ARG_B
ARG_B=0#使用getopt命令来处理脚本的命令行选项和参数,再将处理后的结果重新赋值给位置参数
#eval命令用于将其后的内容作为单个命令读取和执行,这里用于处理getopt命令生成的参数的转义字符
eval set -- `getopt -o a::bc: --long arga::,argb,argc: -n 'getopt_3.sh' -- "$@"`#执行while循环
while true
do
    case "$1" in
        -a|--arga)
            case "$2" in
                "")
                    #当没有给出参数时,使用默认赋值变量ARG_A
                    ARG_A='default value'
                    shift
                    ;;
                *)
                    #当给具有可选参数的选项-a(--arga)指定了参数时,使用指定的参数赋值给变量ARG_A
                    ARG_A=$2
                    shift
                    ;;
            esac
            ;;
        -b|--argb)
            #如果指定了-b选项,则将变量ARG_B赋值为1
            ARG_B=1
            ;;
        -c|--argc)
            case "$2" in
                "")
                    #如果为-c选项没有指定参数,则跳过
                    shift   
                    ;;
                *)
                    #如果为-c选项指定了参数,则将指定的参数赋值给变量ARG_C
                    ARG_C=$2
                    shift
                    ;;
            esac
            ;;
        --)
            #若为双连接字符则退出while循环,表示选项结束
            shift
            break
            ;;
        *)
            #若为其他选项,则显示错误信息,并退出脚本的执行
            echo "Internal error!"
            exit 1
            ;;
    esac    shift
done#打印变量ARG_A、ARG_B、ARG_C的值
echo "ARG_A = $ARG_A"
echo "ARG_B = $ARG_B"
echo "ARG_C = $ARG_C"

从脚本中可以看出,我们可以指定短选项进行运行:

[root@rs1 getopts]# bash getopt_3.sh -s -afsx
getopt_3.sh: invalid option -- 's'
ARG_A = fsx
ARG_B = 0
ARG_C = 
​
[root@rs1 getopts]# bash getopt_3.sh -afsx -b -c 123
ARG_A = fsx
ARG_B = 1
ARG_C = 123
​
[root@rs1 getopts]# bash getopt_3.sh -c 123
ARG_A = 
ARG_B = 0
ARG_C = 123
​
[root@rs1 getopts]# bash getopt_3.sh -c //如果使用-c,就必须添加一个参数
getopt_3.sh: option requires an argument -- 'c'
ARG_A = 
ARG_B = 0
ARG_C = 

看看getopt的使用:

getopt -o a::bc: --long arga::,argb,argc: -n 'getopt_3.sh' -- "$@"

其中:

  • "-o"选项表示让getopt时别哪些短选项。如:-a、-b、-c

  • "--long"选项表示让getopt时别哪些长选项。如:--arga、--argb、--argc

  • "-n"选项告诉getopt程序报告错误时使用什么文件名

"-o"选项所指定的选项字符串遵循如下原则:

  1. 每个字符代表一个选项

  2. 字符后跟一个冒号表示需要一个参数

  3. 字符后跟两个冒号表示可有个可选参数

总结

对于到底使用getopts还是使用getopt,需要根据需求来决定。如果希望Shell脚本支持长选项,则使用getopt,如果考虑到跨平台的兼容性,或者不需要长选项,推荐使用getopts。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值