如何使用 getopts 解析 Linux Shell 脚本选项如何使用 getopts 解析 Linux Shell 脚本选项如何使用 getopts 解析 Linux Shell 脚本选项如何使用 getopts 解析 Linux Shell 脚本选项
  • 文章
  • 正则表达式
    • 工具
  • 登录
找到的结果: {phrase} (显示: {results_count} 共: {results_count_total})
显示: {results_count} 共: {results_count_total}

加载更多搜索结果...

搜索范围
模糊匹配
搜索标题
搜索内容
发表 admin at 2025年2月28日
类别
  • 未分类
标签

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

您是否希望您的 Linux shell 脚本能够更优雅地处理命令行选项和参数? Bash 的 getopts 内置函数让您可以巧妙地解析命令行选项——而且它也很简单。我们告诉你如何。

介绍内置的 getopts

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

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

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

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

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

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

注意:getopts 不是getopt

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

type getopts
type getopt

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

权衡是 getopts 不处理双虚线、长格式的选项名称。因此,您可以使用格式类似于 -w 的选项,但不能使用“---wide-format”。另一方面,如果您的脚本接受选项 -a 、 -b 和 -c 、getopts 让您可以组合它们,例如 -abc、-bca 或 -bac 等等。

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

快速回顾:处理参数值

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

#!/bin/bash

# get the variables one by one
echo "Variable One: $1" 
echo "Variable Two: $2" 
echo "Variable Three: $3"

# loop through the variables
for var in "$@" do 
  echo "$var" 
done

这些参数在脚本中作为变量 $1、$2 或 $3 进行访问。

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

chmod +x variables.sh

如果我们不带参数运行我们的脚本,我们会得到这个输出。

./variables.sh

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

./variables.sh how to geek

正如预期的那样,变量 $1、$2 和 $3 已设置为参数值,我们可以看到它们已打印出来。

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

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

./variables.sh how to geek website

如果我们将两个单词用引号括起来,它们将被视为一个参数。

./variables.sh how "to geek"

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

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

处理选项

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

随着循环的每次迭代,getopts 移动到下一个选项。当没有更多选项时,getopts 返回 false 并且 while 循环退出。

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

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

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

#!/bin/bash

while getopts 'abc' OPTION; do
  case "$OPTION" in 
    a) 
      echo "Option a used" ;;

    b)
      echo "Option b used"
      ;;

    c)
      echo "Option c used"
      ;;

    ?) 
      echo "Usage: $(basename $0) [-a] [-b] [-c]"
      exit 1
      ;;
  esac
done

这是定义 while 循环的行。

while getopts 'abc' OPTION; do

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

echo "Usage: $(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

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

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

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

#!/bin/bash

while getopts ':abc' OPTION; do
  case "$OPTION" in 
    a) 
      echo "Option a used" 
      ;;

    b)
      echo "Option b used"
      ;;

    c)
      echo "Option c used"
      ;;

    ?) 
      echo "Usage: $(basename $0) [-a] [-b] [-c]"
      exit 1
      ;;
  esac
done

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

./options2.sh.sh -d

将 getopts 与选项参数一起使用

要告诉 getopts 一个选项后面将跟一个参数,请在选项字符串中紧跟在选项字母后面放置一个冒号“:”。

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

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

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

#!/bin/bash

while getopts ':ab:c:' OPTION; do

  case "$OPTION" in
    a)
      echo "Option a used"
      ;;

    b)
      argB="$OPTARG"
      echo "Option b used with: $argB"
      ;;

    c)
      argC="$OPTARG"
      echo "Option c used with: $argC"
      ;;

    ?)
      echo "Usage: $(basename $0) [-a] [-b argument] [-c argument]"
      exit 1
      ;;
  esac

done

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

./arguments.sh -a -b "how to geek" -c reviewgeek
./arguments.sh -c reviewgeek -a

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

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

混合选项和参数

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

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

#!/bin/bash

while getopts ':ab:c:' OPTION; do

  case "$OPTION" in
    a)
      echo "Option a used"
      ;;

    b)
      argB="$OPTARG"
      echo "Option b used with: $argB"
      ;;

    c)
      argC="$OPTARG"
      echo "Option c used with: $argC"
      ;;

    ?)
      echo "Usage: $(basename $0) [-a] [-b argument] [-c argument]"
      exit 1
      ;;
  esac

done

echo "Variable one is: $1"

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

./arguments2.sh dave
./arguments2.sh -a dave
./arguments2.sh -a -c how-to-geek dave

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

答案是使用 OPTIND 和 shift 命令。

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

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

OPTIND 在找到和处理选项和参数时对其进行计数。处理完所有选项和参数后,OPTIND 将比选项的数量多一个。因此,如果我们使用 shift 从参数列表中删除 (OPTIND-1) 参数,我们将在 $1 中留下常规参数。

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

#!/bin/bash

while getopts ':ab:c:' OPTION; do
  case "$OPTION" in
    a)
      echo "Option a used"
      ;;

    b)
      argB="$OPTARG"
      echo "Option b used with: $argB"
      ;;

    c)
      argC="$OPTARG"
      echo "Option c used with: $argC"
      ;;

    ?)
      echo "Usage: $(basename $0) [-a] [-b argument] [-c argument]"
      exit 1
      ;;
  esac
done

echo "Before - variable one is: $1"
shift "$(($OPTIND -1))"
echo "After - variable one is: $1"
echo "The rest of the arguments (operands)"

for x in "$@"
do
  echo $x
done

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

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

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

有选择总是好的

在脚本中处理选项及其参数并不需要很复杂。使用 getopts,您可以创建处理命令行选项、参数和参数的脚本,就像符合 POSIX 标准的本机脚本一样。

©2015-2025 艾丽卡 support@alaica.com