shell命令行参数解析工具:getopts

在shell脚本中,对于简单的参数,我们使用$1 $2 来处理即可,但是,如果你的脚本参数非常多,那使用上面的这种方式就非常不合适,你无法清楚地记得每个位置对应的是什么参数。所以,我们可以使用bash内置的getopts

我们先来看看一般的参数处理方法如下:

#!/bin/bash

SOFT_DIR=$1
HOME_DIR=$2
echo $SOFT_DIR
echo $HOME_DIR
-----------------
$ sh test.sh /home/soft /home/soft/benderlfy
/home/soft
/home/soft/benderfly

getopts有如下的调用:

myscript.sh -x -f /etc/soft.conf -r ./source.txt ./destination.txt

我们可以将上述这些选项和参数划分为如下所示的逻辑组:

  • -x-r 都是一个单独的选项,后面不跟参数;
  • -f 也是一个选项,但是这个选项有一个附带的参数 /etc/soft.conf。这个参数通常与选项之间用空格分隔;
  • ./source.txt./destination.txt是不与任何选项关联的两个参数。

如果我们在脚本 myscript.sh 中使用了 getopts来处理命令行选项和参数,那么上述命令还可以写为:

myscript.sh -xrf /etc/soft.conf ./source.txt ./destination.txt

getopts 会识别所有这些选项格式,指定的选项可以是大写或小写字母,或是数字。虽然它也能识别其他字符,但是不推荐使用。

通常情况下,在处理命令行选项和参数时,我们需要多次调用 getoptsgetopts 本身不会更改位置参数的设置,如果我们想要将位置参数移位,必须仍使用 shift 命令来处理位置参数。后面我会举例子

因为当没有内容可以解析时,getopts 会设置一个退出状态 FALSE,所以它很容易在 while 循环中使用:

while getopts ...; do
...
done

getopts 将解析选项和它们可能的参数。它将在第一个非选项参数(不以连字符“”开头的,且不是它前面的任何选项的参数的字符串)的位置停止解析。当遇到双连字符“”(表示选项的结束)时,它也将停止解析。

getopts 会使用到如下 3 个变量:

  • OPTIND:存放下一个要处理的参数的索引。这是 getopts 在调用过程中记住自己状态的方式。同样可以用于移位使用 getopts 处理后的位置参数。OPTIND初始被设置为 1,并且如果你想再次使用 getopts 解析任何内容,都需要将其重置为1
  • OPTARG:这个变量被设置为由 getopts 找到的选项所对应的参数;
  • OPTERR:它的值为 0 或者 1。指示 Bash 是否应该显示由 getopts 产生的错误信息。在每个 Shell 启动时,它的值都被初始化为 1。如果我们不想看到烦人的信息,可以将它的值设置为 0

getopts 命令的基本语法:

getopts OPTSTRING VARNAME [ARGS...]
  • OPTSTRING:告诉 getopts 会有哪些选项和在哪会有参数;
  • VARNAME:告诉 getopts 哪个变量用于选项报告;
  • ARGS:告诉 getopts 解析这些可选的参数,而不是位置参数。

例如,如下的命令告诉 getopts 查找 -f-A-x 选项:

getopts fAx VARNAME

而下面的命令告诉 getopts -A 选项后面会有一个参数:

getopts fA:x VARNAME

默认情况下 getopts 命令是解析当前 Shell 或函数的位置参数。我们可以指定自己的参数让 getopts 来解析。一旦额外的参数指定在了 VARNAME之后,getopts 将不再尝试解析位置参数,而是解析这些额外指定的参数。

getopts 命令还支持两种错误报告的模式,分别为:详细错误报告模式和抑制错误报告模式。对于产品中的脚本,推荐使用抑制错误报告模式,因为这样看起来更专业,不会看到恼人的标准信息。同样它也更容易处理,因为我们以更简单的方法显示了失败的情况。

在详细错误报告模式下,如果 getopts 遇到了一个无效的选项,VARNAME的值会被设置为问号(?),并且变量 OPTARG不会被设置;如果需要的参数没有找到,VARNAME的值同样会被设置为问号(?),变量 OPTARG也不会被设置,并且会打印一个错误信息。

在抑制错误报告模式下,如果 getopts 遇到了一个无效的选项,VARNAME的值会被设置为问号(?),并且变量 OPTARG 会被设置为选项字符;如果需要的参数没找到,VARNAME的值同样会被设置为冒号(:),并且变量 OPTARG中会包含选项字符。

实例1

#! /bin/bash

# 这里仅解析 -a 选项,选项字符串中的第一个字符为冒号(:),表示抑制错误报告
while getopts ":a" opt
do
        case $opt in
                # 匹配 -a 选项
                a)
                        echo "The option -a was triggered!"
                        ;;
                # 匹配其他选项
                \?)
                        echo "Invalid option: -${OPTARG}"
                        ;;
        esac
done

执行结果如下:

shell命令行参数解析工具:getopts

以上的操作中,我们可以发现:

  • 无效的选项不会停止处理:如果我们希望脚本在这种情况下停止运行,我们必须自己做一些完善操作(在适当的位置执行 exit 命令);
  • 多个相同的选项是可能的:如果你想禁止重复的选项,你必须在脚本中做一些检查操作。

实例2

#! /bin/bash

vflag=off
filename=""
output=""

function usage() {
        echo "USAGE:"
        echo "$(basename $0) [-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
                        ;;
                f)
                        filename=$OPTARG
                        if [ ! -f $filename ]
                        then
                                echo "The source file $filename doesn't exist!"
                                exit
                        else
                                echo "$filename"
                        fi
                        ;;
                o)
                        output=$OPTARG
                        if [ ! -d `dirname $output` ]
                        then
                                echo "The output path `dirname $output` doesn't exist!"
                                exit
                        else
                                echo "$output"
                        fi
                        ;;
                h)
                        usage
                        exit
                        ;;
                :)
                        echo "The option -$OPTARG requires an argument."
                        exit 1
                        ;;
                ?)
                        echo "Invalid option: -$OPTARG"
                        usage
                        exit 2
                        ;;
        esac
done

执行结果如下:

shell命令行参数解析工具:getopts

以上的操作中,我们可以发现:

如果 getopts 遇到了一个无效的选项,VARNAME 的值会被设置为问号(?),并且变量 OPTARG 会被设置为选项字符;

如果需要的参数没找到,VARNAME 的值同样会被设置为冒号(:),并且变量 OPTARG 中会包含选项字符。

实例3

getopts字符串中没有跟随:的字母就是开关型选项,不需要指定值,等同于true/false,只要带上了这个参数就是true

getopts识别出各个选项之后,就可以配合case进行操作。操作中,有两个”常量”,一个是OPTARG,用来获取当前选项的值;另外一个就是OPTIND,表示当前选项在参数列表中的位移。case的最后一项是?,用来识别非法的选项,进行相应的操作,我们的脚本中输出了帮助信息。

当选项参数识别完成以后,我们就能识别剩余的参数了,我们可以使用shift进行位移,抹去选项参数。

#!/bin/bash

usage() {
    echo "Usage:"echo "  test.sh [-j JAVA_DIR] [-m MAVEN_DIR]"echo "Description:"echo "    JAVA_DIR, the path of java."echo "    MAVEN_DIR, the path of maven."exit -1
}

upload="false"

echo $OPTIND

while getopts 'j:m:u' OPT; docase $OPT in
        j) JAVA_DIR="$OPTARG";;
        m) MAVEN_DIR="$OPTARG";;
        u) upload="true";;
        h) usage;;
        ?) usage;;
    esac
done

echo $OPTIND
shift $(($OPTIND - 1))
echo $1

---------------
$ sh test.sh -j /home/soft/java -m /home/soft/maven otherargs
1
5
otherargs

sh test.sh -j /home/soft/java -m /home/soft/maven -u otherargs
1
6
otherargs

在上面的脚本中,我们位移的长度等于case循环结束后的OPTIND - 1OPTIND的初始值为1,当选项参数处理结束后,其指向剩余参数的第一个。getopts在处理参数时,处理带值的选项参数,OPTIND加2;处理开关型变量时,OPTIND则加1。

可以简单的理解为shift $((OPTIND -1))表示参数位置移动到非选项参数的第一个位置即上面例子中的otherargs

以上就是对getopts的简单介绍。

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论