getopts简易教程(Small getopts tutorial)译文

getopts简易教程

当你想用一种专业的方式解析命令行参数时,getopts就是要选择的工具。和它的旧版本兄弟命令getopt不同(注意没有s!),getopts是shell内置命令。高级地方表现在

  • 你不需要通过一个外部命令传递参数
  • getopts可以很容易的设置一些你能用于解析参数的变量(对于一个外部程序来说这是不可能的!)
  • 你不必再处理过去一些使用getopt时的一些bug实现(空格, …)
  • getopts已经在POSIX®定义

一些解析位置参数的其他方法(不用getopt(s))在这里介绍了: 如何处理位置参数.

注意getopts不能解析GNU风格的长选项(--myoption)或XF86风格的长选项(-myoption)!

介绍

术语

需要先了解一下我们这里探讨的事情,所以让我们来看一个范例… 来看一下下面这行命令:

mybackup -x -f /etc/mybackup.conf -r ./foo.txt ./bar.txt

所有这些都叫位置参数,但是你可以把他们分成一些逻辑组:

  • -x是一个选项, 一个标识, 一个开关: 一个字符, 有一个前引导短横杠(-)
  • -f也是一个选项,但是这个选项有一个附加参数(传递给-f选项的参数): /etc/mybackup.conf。这个参数通常与它的参数分开(使用一个空格或其他分隔符), 但这不是必须的, -f/etc/mybackup.conf也是合法的。
  • -r依赖于配置。在这个范例中,-r不需要参数,所以它是独立的选项,像-x
  • ./foo.txt和./bar.txt是剩余的参数,不与任何选项关联。这些通常用作聚合参数(比如你指定给cp(1)的文件名)或者不需要选项的参数,因为这是程序的预定义格式(就像你传递给文本编辑器的文件名参数,用于打开和显示文本 - 为何还需要一个额外的开关呢?). POSIX®调用它们作为操作对象

让你体会一下为什么getopts很有用: 上面的命令可以像这样读取…

mybackup -xrf /etc/mybackup.conf ./foo.txt ./bar.txt

…使用自己的代码去解析很困难。而getopts可以认出所有常见的选项格式。

选项标识可以有大小写,也可以是数字。甚至是其他可识别字符,但是并不推荐这么做(可用性差而且特殊字符可能会出问题)。

原理

一般你需要调用getopts好几次。每次会使用"下一个"位置参数(和一个可能的附加参数),如果解析成功,会给你返回结果。getopts不会改变位置参数的设定 —— 如果你想要shift掉参数,你必须手工处理:

shift $((OPTIND-1))
# now do something with $@

因为getopts在没有要解析的参数剩余时会返回退出状态FALSE,所以可以很容易的在while循环使用:

while getopts ...; do
  ...
done

getopts将会解析选项和他们可能的参数。遇到第一个非选项的参数时将会停止解析(一个不以连字符(-)开头的字符串,这不是前面任何一个选项的参数)。同样也会在看到--(双连字符)时停止解析,因为这个含义是选项终止。

有用的变量

变量 描述
OPTIND 保存下一个要解析的参数的指针。这就是getopts如何"记住"自己的状态和回调请求。同样可以用于在getopts处理过之后shift掉位置参数。OPTIND初始为1, 如果你想要再次使用getopts解析任何参数时需要重新设置为1
OPTARG 这个变量设置为被getopts发现的选项的参数。同样包含了未知的选项标记
OPTERR (可选值0或1)表明Bash是否应该显示getopts内置的错误信息。该值在每个shell启动的时候会被初始化为1 - 所以如果你不想看到烦人的信息请务必设置为0!

getopts同样使用这些变量用于错误报告(they're set to value-combinations which arent possible in normal operation).#括号里面的不会翻译,预留

指明你想要做的

getopts的基本语法是:

getopts OPTSTRING VARNAME [ARGS...]

解释:

OPTSTRING 告诉getopts期望哪个选项和期望的参数在哪里(参考下面的)
VARNAME 告诉getopts哪个shell变量用于选项报告
ARG 告诉getopts将这些解析为附加单词而不是位置参数

option-string

option-string告诉getopts期望哪个选项,并且哪个选项必须有参数。语法非常简单 – 每个字母就是选项名本身,下面这个范例告诉getopts寻找-f, -A和-x:

getopts fAx VARNAME

当你想让getopts对某个选项期望一个参数时,只是在这个选项标记后面放置一个:(冒号)。如果你想让-A期望一个参数(例如变成 -A SOMETHING),只需要:

getopts fA:x VARNAME

如果option-string首个字母是:(冒号),通常是荒谬的,因为没有任何选项在它之前,这种情况下,getopts会切换到"静默错误报告"模式。在产品脚本中,这通常就是你想要的结果(自行抓取错误信息处理,不要被烦人的信息所干扰)。

自定义参数解析

getopts工具会默认解析当前shell或函数的位置参数(意味着它解析的是”$@“)。

你可以给出你自己的一组参数解析。当附加参数在VARNAME参数之后给出时,getopts并不试图解析这些位置参数。

用这种方式,你可以按照你喜欢的方式解析任何选项,这里是一个数组的范例:

while getopts :f:h opt "${MY_OWN_SET[@]}"; do
  ...
done

不带这些附加参数调用getopts的方式等同于显式的使用”$@“调用:

getopts ... "$@"

错误报告

关于错误报告,getops可以在两种模式下运行:

  • 详尽模式
  • 安静模式

对于产品脚本我建议使用静默模式,因为这样看起来更专业,你不想看到更多烦人的信息。同样也更容易处理,失败的用例都以更简单的方式显示。

详尽模式

invalid option VARNAME is set to ? (quersion-mark) and OPTARG is unset
required argument not found VARNAME is set to ? (quersion-mark), OPTARG is unset and an error message is printed

安静模式

invalid option VARNAME is set to ? (question-mark) and OPTARG is set to the (invalid) option character
required argument not found VARNAME is set to : (colon) and OPTARG contains the option-character in question

开搞

第一个例子

足够说明一些问题!

让我们先看一个非常简单的实例: 只有一个期望的选项(-a),没有任何参数。同样我们用带:(冒号)的option string为了禁用详尽错误显示:

#!/bin/bash

while getopts ":a" opt; do
  case $opt in
    a)
      echo "-a was triggered!" >&2
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      ;;
  esac
done

我把这些内容放到一个文件go_test.sh,就是你接下来看到的范例中的名字。

来让我们做一些测试:

不带任何参数调用

$ ./go_test.sh
$ 

什么都没发生?对的,getopts没看到任何合法或非法的选项(前面有短横线的字母),所以不会触发。

以非选项的参数调用

$ ./go_test.sh /etc/passwd
$ 

还是 — 什么都没发生。非常类似的用例: getopts没看到任何合法或非法的选项(前面有短横线的字母),所以不会触发。

传递给你的脚本的参数当然可以用$1 - ${N}获取。

用选项参数调用

现在让我们触发一下getopts: 提供选项。

首先,来一个非法的:

$ ./go_test.sh -b
Invalid option: -b
$ 

和预期的一样,getopts不允许这个选项,像上面说的一样: It placed ? into $opt and the invalid option character (b) into $OPTARG. 我们的用例就验证了这一点。

现在,来一个合法的(-a):

$ ./go_test.sh -a
-a was triggered!
$ 

你看到了,这个探测结果运行的很完美。在我们的用例a选项放在了变量$opt中。

当然在调用的时候可以组合有效和无效的选项:

$ ./go_test.sh -a -x -b -c
-a was triggered!
Invalid option: -x
Invalid option: -b
Invalid option: -c
$ 

最后,自然也可以多次给出我们的选项:

$ ./go_test.sh -a -a -a -a
-a was triggered!
-a was triggered!
-a was triggered!
-a was triggered!
$ 

最后一个例子让我们需要考虑到几点:

  • 无效选项并不停止处理过程: 如果你想停掉脚本,你必须自己处理(在正确位置退出)
  • 多个相同选项是可行的: 如果你你不允许这么做,你必须自己检测(例如,设置一个变量或其他方式)

一个带参数的选项

让我们把上面的例子扩展一下。只有一点点:

  • -a现在需要一个参数-a now takes an argument
  • 遇到错误,解析过程以exit 1退出
#!/bin/bash

while getopts ":a:" opt; do
  case $opt in
    a)
      echo "-a was triggered, Parameter: $OPTARG" >&2
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

让我们继续做和最后一个范例同样的测试:

不用任何参数调用

$ ./go_test.sh
$ 

像上面一样,什么都没发生。就没触发。

以非选项的参数调用

$ ./go_test.sh /etc/passwd
$ 

非常类似的情况: 没有触发。

带参数选项调用

非法选项:

$ ./go_test.sh -b
Invalid option: -b
$ 

和期望的一样,像上面一样,getopts不允许这个选项,和程序算法吻合。

合法选项,但是不带强制要求的参数:

$ ./go_test.sh -a
Option -a requires an argument.
$ 

选项OK,但是丢掉了一个参数。

让我们提供这个参数:

$ ./go_test.sh -a /etc/passwd
-a was triggered, Parameter: /etc/passwd
$

参考资料

  • Internal: Handling positional parameters
  • Internal: The case statement
  • Internal: The while-loop
  • POSIX getopts(1) and getopt(3)

你可能感兴趣的:(bash,getopts)