Bash脚本小技巧:使用getopts优雅处理命令行选项

命令行参数是控制脚本行为的一种好方法,无需依赖其他文件或环境变量。虽然 shell 提供了使用位置参数处理命令行选项的简单快捷方法,但对于更大和更复杂的脚本,这种方法很快就变得繁琐。

file

特殊的 shell 变量($0 … $9)存储命令行参数的内容,可以在脚本中使用。这要求用户按照正确的顺序传递参数,每个参数的使用可能对脚本用户来说并不很明显。

getopts 是 POSIX-compliant shell(例如 Bash)中的内置实用程序,它允许你简单地为你的脚本定义选项和参数。

它提供了一个简单的接口,减少了脚本用户的复杂性。它允许你定义选项,如 -f,并指定它们是否需要参数。

getopts 的语法

getopts optstring name [ arg ]
  • optstring 包含了被识别的选项字母。如果字母后面跟着一个冒号,那么这个选项就预期有一个参数。
  • name 是接收选项的变量。
  • arg 是要处理的可选参数列表。

file

getopts 使用内置变量 OPTIND(选项索引)和 OPTARG(选项参数)来处理处理过程并保存选项值。当脚本开始时,OPTIND 被初始化为 1。

每次调用 getopts 并存在一个选项时,它都会执行以下操作:

  • 将下一个选项放在变量 name 中。
  • 将要处理的下一个选项的索引设置到内置的 OPTIND 中。
  • 将选项值设置到内置的 OPTARG 中,或者如果没有提供,则初始化 OPTARG 为 ""(空字符串)。

getopts 的基本使用

这是一个定义了一个单一选项 -h 的简单脚本。

#!/bin/bash

function print_usage() {
       echo "simpleObjectColor [OPTIONS VALUE]"
       echo -e "Supported options and values: \n"
       echo "-h                Print usage"

       exit
}

## Define option string
optstring="h"

while getopts ${optstring} arg; do
    case ${arg} in
            h) print_usage;;
            ?) echo -e "Unrecognized option.";;
    esac
done

现在,让我们看看这个脚本是如何工作的:

optstring="h" 定义了一个只有一个标记 -h 的选项字符串,它不期望有任何值。

while getopts ${optstring} opt 在一个循环中解析选项字符串,直到没有更多的选项。

我们使用 -h 标记执行脚本。它打印使用字符串并退出。

ubuntu@linuxopsys:~$./simpleObjectColor.sh -h

simpleObjectColor [OPTIONS VALUE]
Supported options and values:

-h                Print usage

getopts 与 getopt 的比较

不像 getopts,getopt 是一个为不同的 Unix 和类 Unix 系统创建的程序,而 getopts 是许多 POSIX-compliant shell(如 Bash)中的内置 shell 命令。

getopts 和 getopt 之间有许多功能上的差异:

getopts getopt
只处理使用单个破折号(-)后跟单个字符写的短选项(例如:-h)。 处理短选项和长选项,长选项使用两个破折号(–)后跟一个单词(例如:–help)。
有限的错误处理能力。依赖用户确保输入有效。 对错误处理的广泛支持。能够报告不支持的选项或缺失的选项值。
是 POSIX-compliant shell 的内置实用程序。默认在大多数 shell 中可用。 默认并非在所有系统上都可用。由于大规模的采用,现在可以在所有主要的发行版上安装。

getopts 的示例

使用多个选项

让我们看看如何编写一个可以输入多个选项并处理每个选项对应的值的脚本。

下面的脚本引入了 3 个更多的选项:-t,-n 和 -c,它们也期望有值。

#!/bin/bash

function print_usage() {
       echo "simpleObjectColor [OPTIONS VALUE]"
       echo -e "Supported options and values: \n"
       echo "-h               Print usage"
       echo "-t               Type of food (fruit/vegetable/dish)"
       echo "-n               Name of the food (apple/brinjal/pizza)"
       echo "-c               Color of the food"

       exit
}

## Define option string
optstring="ht:n:c:"
i=1

## OPTIND
echo -e "Option index at start: $OPTIND\n"

## Handle different options
while getopts ${optstring} arg; do

    i=$(($i+1))
    case ${arg} in
           h) print_usage;;
            t)  echo -e "Nype: ${OPTARG}";;
            n)  echo -e "Tame: ${OPTARG}";;
            c)  echo -e "Color: ${OPTARG}";;
            ?)  echo -e "Unrecognized option.";;
    esac

    ## OPTIND
    echo -e "Option index after $i getopts invocations: $OPTIND\n"
done

我们执行这个脚本,带有所有 3 个支持的标记以及每个标记的值。

$./simpleObjectColor.sh -t fruit -n apple -c red
Option index at start: 1

"Type: fruit"
Option index after 2 getopts invocations: 3

"Name: apple"
Option index after 3 getopts invocations: 5

"Color: red"
Option index after 4 getopts invocations: 7

Option index at end: 7

现在,让我们看看这个脚本是如何工作的:

“t:n:c:" 添加了期望参数的标记 -t,-n 和 -c,这是由于每个字符后面都有一个 ‘:’。

$OPTIND 被初始化 = 1。

在第 1 次迭代中,它在 $arg 中解析 ‘t’,在 $OPTARG 中解析 "fruit"。

在第 2 次迭代中,它在 $arg 中解析 ‘n’,在 $OPTARG 中解析 "apple"。

在第 3 次迭代中,它在 $arg 中解析 ‘c’,在 $OPTARG 中解析 "red"。

最后 $OPTIND = 7,因为我们传递了 6 个参数(3 个标记,3 个标记值)给脚本。

使用 shift 命令

shift 是另一个常与 getopts 一起使用的内置命令,它可以有效地将命令行选项和值向左移动,从列表中删除它们。

这非常有用,因为它有助于从参数列表中删除已处理的命令行选项。

让我们看看如何在 getopts 中使用 shift 命令。

#!/bin/bash

function print_usage() {
       echo "simpleObjectColor [OPTIONS VALUE]"
       echo -e "Supported options and values: \n"
       echo "-h               Print usage"
       echo "-t               Type of food (fruit/vegetable/dish)"
       echo "-n               Name of the food (apple/brinjal/pizza)"
       echo "-c               Color of the food"

       exit
}

## Define option string
optstring="ht:n:c:"
i=1

## OPTIND
echo -e "Option index at start: $OPTIND\n"

## Handle different options
while getopts ${optstring} arg; do

    i=$(($i+1))
    case ${arg} in
           h) print_usage;;
            t)  echo -e "Nype: ${OPTARG}";;
            n)  echo -e "Tame: ${OPTARG}";;
            c)  echo -e "Color: ${OPTARG}";;
            ?)  echo -e "Unrecognized option.";;
    esac

    ## OPTIND
    echo -e "Option index after $i getopts invocations: $OPTIND\n"
Done

shift  $((OPTIND-1))
echo "The remaining arguments to be processed: $@"

执行带有一个附加参数的脚本:

$./simpleObjectColor.sh -t fruit -n apple -c red 85
Option index at start: 1

"Type: fruit"
Option index after 2 getopts invocations: 3

"Name: apple"
Option index after 3 getopts invocations: 5

"Color: red"
Option index after 4 getopts invocations: 7

Option index at end: 7
The remaining arguments to be processed: 85

执行 shift 命令后,getopts 处理后,$@ 变量保存了剩余的命令行参数。

在这种情况下,参数列表($@)有效地变成了 = (85),这可以在 getopts 循环外进一步处理。

错误处理

在 shell 脚本中使用 getopts,如果它遇到一个意外的选项,或者如果它找不到一个预期的选项值,它将向标准错误打印一个错误消息,并以非零状态码退出。

默认行为

考虑以下脚本:

$ cat errorHandling.sh

#!/bin/bash

## Define option string
optstring="n:"

getopts ${optstring} arg
case ${arg} in
           n) echo -e "Name: ${OPTARG}";;
           ?) echo -e "Unrecognized option, arg: $arg, OPTARG: $OPTARG";;
esac

意外的选项:向脚本传递一个无效的标记 -m,会使脚本退出并报错。

$./errorHandling.sh -m Giraffe
./errorHandling.sh: illegal option -- m

无效的值:相反,如果传递了正确的标记 -n 但没有指定值,它也会使脚本退出并报错。

$./errorHandling.sh -n
./errorHandling.sh: option requires an argument -- n
静默错误处理

如果我们在 optstring 的开始处放一个冒号(’:’),getopts 就会运行在 "静默错误检查模式"。如果脚本遇到一个意外的选项或一个无效的值,它不会终止或打印任何详细的错误。因此,它期望在脚本本身中处理错误。

现在,考虑以下脚本,它在现有的 optstring 前面加了一个 ‘:’。

$ cat errorHandling.sh
#!/bin/bash

## Define option string
optstring=":n:"

getopts ${optstring} arg
case ${arg} in
           n) echo -e "Name: ${OPTARG}";;
           ?) echo -e "Unrecognized option, arg: $arg, OPTARG: $OPTARG";;
esac

意外的选项:在传递一个无效的标记 -m 时,getopts 设置 $arg=’?’ 并设置 $OPTARG=’m’。

$./errorHandling.sh -m
Unrecognized option. arg: ?, OPTARG: m

无效的值:相反,如果传递了正确的标记 -n 但没有指定值,getopts 设置 $arg=’:’ 并设置 $OPTARG=’n’。

$./errorHandling.sh -n
Unrecognized option. arg: :, OPTARG: n

总的来说,getopts 对于具有少量参数和简单值的简单脚本很有用。但是对于更大和更复杂的脚本,需要更好的错误处理,getopt 可能是处理和处理 CLI 参数的更好选择。

揭秘Bash脚本调试:你不知道的秘密工具和技巧
python获取随机字符串几种方法

发表我的评论

电子邮件地址不会被公开。 必填项已用*标注

51 + 4 =

ajax-loader