如何使用 getopts 解析 Linux Shell 脚本选项

已发表: 2022-06-25
显示终端文本的笔记本电脑屏幕。
fatmawati achmad zaenuri/Shutterstock.com

您是否希望您的 Linux shell 脚本能够更优雅地处理命令行选项和参数? 内置的 Bash getopts可让您巧妙地解析命令行选项,而且也很容易。 我们向您展示如何。

介绍内置的 getopts

传递给 Bash 脚本是一件非常简单的事情。 您从命令行或另一个脚本调用您的脚本,并在脚本名称后面提供您的值列表。 这些值可以在脚本中作为变量访问,从第一个变量的$1开始,第二个变量的$2开始,依此类推。

但是,如果您想将选项传递给脚本,情况很快就会变得更加复杂。 当我们说选项时,我们指的是像ls这样的程序可以处理的选项、标志或开关。 它们前面有一个破折号“ - ”,通常作为程序的指示器来打开或关闭其功能的某些方面。

如何在 Linux 上使用 ls 命令列出文件和目录
相关如何在 Linux 上使用 ls 命令列出文件和目录

ls命令有超过 50 个选项,主要与格式化其输出有关。 -X (按扩展名排序)选项按文件扩展名的字母顺序对输出进行排序。 -U (未排序)选项按目录顺序列出。

选项就是这样——它们是可选的。 您不知道用户将选择使用哪些选项(如果有的话),您也不知道他们在命令行中列出它们的顺序。 这增加了解析选项所需的代码的复杂性。

如果您的某些选项带有一个参数,称为选项参数,事情会变得更加复杂,例如, ls -w (width) 选项后面需要一个数字,表示输出的最大显示宽度。 当然,您可能会将其他参数传递到脚本中,这些参数只是数据值,根本不是选项。

幸运的是getopts为您处理了这种复杂性。 而且因为它是内置的,它在所有具有 Bash shell 的系统上都可用,因此无需安装任何东西。

注意:getopts 不是 getopt

有一个名为getopt的旧实用程序。 这是一个小型实用程序,不是内置的。 有许多不同版本的getopt具有不同的行为,而内置的getops遵循 POSIX 准则。

 输入 getopts
 输入 getopt 

使用 type 命令查看 getop 和 getops 的区别

因为getopt不是内置的,所以它不共享getopts的一些自动好处,例如明智地处理空格。 使用getopts ,Bash shell 正在运行您的脚本,而 Bash shell 正在执行选项解析。 您无需调用外部程序来处理解析。

权衡是getopts不处理双破折号的长格式选项名称。 因此,您可以使用-w格式的选项,但不能使用“ ---wide-format ”。 另一方面,如果您有一个接受选项-a-b-c的脚本, getopts可以让您像-abc-bca-bac等那样组合它们。

我们将在本文中讨论和演示getopts ,因此请确保将最后的“s”添加到命令名称中。

相关:如何在 Windows 命令行上转义文件路径中的空格

快速回顾:处理参数值

此脚本不使用-a-b等虚线选项。 它确实接受命令行上的“普通”参数,并且这些参数在脚本内部作为值访问。

 #!/bin/bash

# 一一获取变量
echo "变量一:$1" 
echo "变量二:$2" 
echo "变量三:$3"

# 循环遍历变量
对于 " $@" 中的 var 做
回声“$ var” 
完毕

这些参数在脚本内部作为变量$1$2$3访问。

将此文本复制到编辑器中并将其保存为名为“variables.sh”的文件。 我们需要使用chmod命令使其可执行。 您需要对我们讨论的所有脚本执行此步骤。 每次只需替换相应脚本文件的名称即可。

 chmod +x 变量.sh 

使用 chmod 命令使脚本可执行

如果我们在没有参数的情况下运行我们的脚本,我们会得到这个输出。

 ./variables.sh 

运行不带参数的脚本

我们没有传递任何参数,因此脚本没有要报告的值。 这次我们提供一些参数。

 ./variables.sh 如何极客

运行一个以三个单词作为参数的脚本

正如预期的那样,变量$1$2$3已设置为参数值,我们看到这些打印出来了。

这种一对一的参数处理方式意味着我们需要提前知道会有多少个参数。 脚本底部的循环并不关心有多少参数,它总是循环遍历它们。

如果我们提供第四个参数,它不会分配给变量,但循环仍然会处理它。

 ./variables.sh 如何访问极客网站

将四个参数传递给只能处理三个的脚本

如果我们在其中两个单词周围加上引号,它们将被视为一个参数。

 ./variables.sh 如何“极客” 

引用两个命令行参数以将它们视为一个参数

如果我们需要我们的脚本来处理选项、带参数的选项和“普通”数据类型参数的所有组合,我们需要将选项与常规参数分开。 我们可以通过将所有选项(带或不带参数)放在常规参数之前来实现。

但是,在我们能走路之前,我们不要跑。 让我们看一下处理命令行选项的最简单情况。

处理选项

我们在while循环中使用getopts 。 循环的每次迭代都对传递给脚本的一个选项起作用。 在每种情况下,变量OPTION都设置为由getopts标识的选项。

在循环的每次迭代中, getopts都会转到下一个选项。 当没有更多选项时, getopts返回false并退出while循环。

如何在 Bash 脚本中使用案例语句
相关如何在 Bash 脚本中使用案例语句

OPTION变量与每个 case 语句子句中的模式相匹配。 因为我们使用的是 case 语句,所以在命令行中提供选项的顺序并不重要。 每个选项都被放入 case 语句中,并触发相应的子句。

case 语句中的各个子句使在脚本中执行特定于选项的操作变得容易。 通常,在实际脚本中,您会在每个子句中设置一个变量,这些变量将在脚本中进一步充当标志,允许或拒绝某些功能。

将此文本复制到编辑器中并将其保存为名为“options.sh”的脚本,并使其可执行。

 #!/bin/bash

而getopts'abc'选项; 做
  案例“$OPTION”在 
    一个) 
      echo "使用的选项" ;;

    b)
      echo "使用了选项 b"
      ;;

    C)
      echo "使用了选项 c"
      ;;

    ?) 
      echo "用法:$(basename $0) [-a] [-b] [-c]"
      1号出口
      ;;
  经社理事会
完毕

这是定义 while 循环的行。

 而getopts'abc'选项; 做

getopts命令后跟选项字符串。 这列出了我们将用作选项的字母。 只有此列表中的字母可用作选项。 所以在这种情况下, -d将是无效的。 这会被?)子句困住,因为getopts返回一个问号“ ? ” 对于一个未识别的选项。 如果发生这种情况,正确的用法将打印到终端窗口:

 echo "用法:$(basename $0) [-a] [-b] [-c]"

按照惯例,在这种类型的正确使用消息中将选项括在括号“ [] ”中意味着该选项是可选的。 basename 命令从文件名中删除任何目录路径。 脚本文件名在 Bash 脚本中保存在$0中。

让我们将此脚本与不同的命令行组合一起使用。

 ./options.sh -a
 ./options.sh -a -b -c
 ./options.sh -ab -c
 ./options.sh -cab 

测试可以接受开关类型命令行选项的脚本

正如我们所见,我们所有的选项测试组合都被正确解析和处理。 如果我们尝试一个不存在的选项怎么办?

 ./options.sh -d 

shell 和脚本报告了一个无法识别的选项

触发了用法子句,这很好,但我们也从 shell 中得到了错误消息。 这对您的用例可能或可能无关紧要。 如果您从另一个必须解析错误消息的脚本调用脚本,那么如果 shell 也生成错误消息,则会变得更加困难。

关闭 shell 错误消息非常容易。 我们需要做的就是将冒号“ : ”作为选项字符串的第一个字符。

编辑您的“options.sh”文件并添加一个冒号作为选项字符串的第一个字符,或者将此脚本保存为“options2.sh”,并使其可执行。

 #!/bin/bash

而 getopts ':abc' 选项; 做
  案例“$OPTION”在 
    一个) 
      echo "使用的选项" 
      ;;

    b)
      echo "使用了选项 b"
      ;;

    C)
      echo "使用了选项 c"
      ;;

    ?) 
      echo "用法:$(basename $0) [-a] [-b] [-c]"
      1号出口
      ;;
  经社理事会
完毕

当我们运行它并产生错误时,我们会收到我们自己的错误消息,而没有任何 shell 消息。

 ./options2.sh.sh -d 

仅由脚本报告的无法识别的选项

使用带有选项参数的 getopts

要告诉getopts一个选项后面会跟一个参数,请在选项字符串中的选项字母后面加上一个冒号“ : ”。

如果我们在选项字符串中的“b”和“c”后面加上冒号, getopt将期望这些选项的参数。 将此脚本复制到您的编辑器中并将其保存为“arguments.sh”,并使其可执行。

请记住,选项字符串中的第一个冒号用于抑制 shell 错误消息——它与参数处理无关。

getopt处理带有参数的选项时,该参数被放置在OPTARG变量中。 如果您想在脚本的其他地方使用此值,则需要将其复制到另一个变量。

 #!/bin/bash

而getopts':ab:c:'选项; 做

  案例“$OPTION”在
    一个)
      echo "使用的选项"
      ;;

    b)
      argB="$OPTARG"
      echo "选项 b 用于:$argB"
      ;;

    C)
      argC="$OPTARG"
      echo "选项 c 用于:$argC"
      ;;

    ?)
      echo "用法:$(basename $0) [-a] [-b 参数] [-c 参数]"
      1号出口
      ;;
  经社理事会

完毕

让我们运行它,看看它是如何工作的。

 ./arguments.sh -a -b "如何极客" -c reviewgeek
 ./arguments.sh -c reviewgeek -a 

测试可以处理选项参数的脚本

所以现在我们可以处理带有或不带参数的选项,而不管它们在命令行中给出的顺序。

但是常规参数呢? 我们之前说过,我们知道我们必须在任何选项之后将它们放在命令行上。 让我们看看如果我们这样做会发生什么。

混合选项和参数

我们将更改之前的脚本以包含更多行。 当while循环退出并且所有选项都已处理后,我们将尝试访问常规参数。 我们将打印出$1中的值。

将此脚本另存为“arguments2.sh”,并使其可执行。

 #!/bin/bash

而getopts':ab:c:'选项; 做

  案例“$OPTION”在
    一个)
      echo "使用的选项"
      ;;

    b)
      argB="$OPTARG"
      echo "选项 b 用于:$argB"
      ;;

    C)
      argC="$OPTARG"
      echo "选项 c 用于:$argC"
      ;;

    ?)
      echo "用法:$(basename $0) [-a] [-b 参数] [-c 参数]"
      1号出口
      ;;
  经社理事会

完毕

echo "变量一是:$1"

现在我们将尝试一些选项和参数的组合。

 ./arguments2.sh 戴夫
./arguments2.sh -a 戴夫
./arguments2.sh -a -c how-to-geek 戴夫

无法访问接受选项参数的脚本中的标准参数

所以现在我们可以看到问题所在。 一旦使用了任何选项,变量$1以后就会被选项标志及其参数填充。 在最后一个示例中, $4将保存参数值“dave”,但是如果您不知道要使用多少选项和参数,如何在脚本中访问它?

答案是使用OPTINDshift命令。

shift命令从参数列表中丢弃第一个参数(无论其类型如何)。 其他参数“shuffle down”,因此参数 2 变为参数 1,参数 3 变为参数 2,依此类推。 所以$2变成$1$3变成$2 ,依此类推。

如果您为shift提供一个数字,它将从列表中删除那么多参数。

OPTIND在发现和处理选项和参数时对其进行计数。 处理完所有选项和参数后, OPTIND将比选项数大一。 因此,如果我们使用 shift 将(OPTIND-1)参数从参数列表中删除,我们将在$1之后保留常规参数。

这正是这个脚本所做的。 将此脚本另存为“arguments3.sh”并使其可执行。

 #!/bin/bash

而getopts':ab:c:'选项; 做
  案例“$OPTION”在
    一个)
      echo "使用的选项"
      ;;

    b)
      argB="$OPTARG"
      echo "选项 b 用于:$argB"
      ;;

    C)
      argC="$OPTARG"
      echo "选项 c 用于:$argC"
      ;;

    ?)
      echo "用法:$(basename $0) [-a] [-b 参数] [-c 参数]"
      1号出口
      ;;
  经社理事会
完毕

echo "之前 - 变量一是:$1"
转移“$(($OPTIND -1))”
echo "之后 - 变量一是:$1"
echo "其余参数(操作数)"

对于“$@”中的 x
做
  回声 $x
完毕

我们将混合使用选项、参数和参数来运行它。

 ./arguments3.sh -a -c how-to-geek “dave dee” dozy beaky mick tich 

在接受选项参数的脚本中正确访问标准参数

我们可以看到,在调用shift之前, $1持有“-a”,但在 shift 命令之后, $1持有我们的第一个非选项、非参数参数。 我们可以像在没有选项解析的脚本中一样轻松地遍历所有参数。

有选择总是好的

在脚本中处理选项及其参数不需要很复杂。 使用getopts ,您可以创建处理命令行选项、参数和参数的脚本,就像 POSIX 兼容的原生脚本一样。