原文地址 : getopts 解析bash 命令行参数
Shell脚本中的一项常见任务是解析命令行参数。 Bash提供了内置函数getopts来完成此任务。本教程说明了如何使用内置的getopts函数来解析bash脚本的参数和选项。

getopts 语法

  1. getopts optstring name [args]

总共有三个参数:

  • optstring
    需要识别选项列表。说明哪些选项有效,以字母顺序列出。例如,字符串 ht 表示选项 -h-t 有效。
    如果字符后面跟一个 : , 表示该选项期望有一个参数,与选项以空白字符分割。
    ?: 不能作为选项字符使用

  • name
    是一个变量,用来填充要处理的选项。

  • args
    是要处理的参数和选项的列表。如果未提供,则默认为提供给应用程序($@)的参数和选项。您可以提供第三个参数,以使用getopts解析您提供的参数和选项的任何列表。

运行

每次调用的时候,getopts都会将选项【h,t等】放置在变量name中,如果不存在则将其初始化。并且将选项的索引,放置在变量OPTIND中,每次调用脚本时, OPTIND 都会初始化为1。当选项options要求参数时,getopts将参数放置在变量OPTARG。OPTIND保留最后一次调用getopts的选项的索引值,shell不会重置OPTIND的值,通常的做法是在处理循环结束时调用shift命令,从$@处删除已处理的选项。

  1. shift $((OPTIND -1))

当处理到最后一个选项时,getopts 会返回一个大于0的值,并退出。OPTIND 会被设置为第一个无选项实参的索引值, name 会被设置为 ?。

getopts 以两种方式处理错误。如果 optsting 的第一个参数是 :, 或者 OPTERR 设置为 0, 静默错误处理,不返回信息。 OPTERR 处理优先级更高。

如果选项【option】无效,则getopts把name设置为 ?,如果错误静默处理:则找到的选项字符将放置在OPTARG中,并且不会打印错误消息;否则则会显示错误消息并 unset OPTARG。

如果要求的实参【argument】未找到, 静默处理是把: 设置为名称,把选项放在 OPTARG中,非静默处理则set name = ?,unset OPTARG, 打印错误。

如果getopts 找到 options, 不管是未定义,还是已定义的,都返回true; 如遇到错误,或到结尾,返回false。【方便循环处理options】

example

在以下循环中,opt将保存由getopts解析的当前选项的值。

  1. while getopts ":ht" opt; do
  2. case ${opt} in
  3. h ) # process option h
  4. ;;
  5. t ) # process option t
  6. ;;
  7. \? ) echo "Usage: cmd [-h] [-t]"
  8. ;;
  9. esac
  10. done

带参选项解析
本身具有参数的选项以:表示。选项的参数放置在变量OPTARG中。在下面的示例中,选项t带有一个参数。提供参数后,我们会将其值复制到变量target。如果未提供任何参数,则getopts将opt设置为:我们可以通过捕获:case并打印适当的错误消息来识别此错误情况。

  1. while getopts ":t:" opt; do
  2. case ${opt} in
  3. t )
  4. target=$OPTARG
  5. ;;
  6. \? )
  7. echo "Invalid option: $OPTARG" 1>&2
  8. ;;
  9. : )
  10. echo "Invalid option: $OPTARG requires an argument" 1>&2
  11. ;;
  12. esac
  13. done
  14. shift $((OPTIND -1))


解析嵌套的参数和选项
我们以pip 为例, 实现一个带有子命令,子选项的shell脚本。

  1. > pip -h
  2. Usage:
  3. pip <command> [options]
  4. Commands
  5. install Install a Python package.
  6. General Options:
  7. -h Show help.


首先使用getopts通过以下while循环来解析-h选项。在其中,我们使用?捕获无效的选项,并用移位$((OPTIND -1))移位所有已处理的参数。

  1. while getopts ":h" opt; do
  2. case ${opt} in
  3. h )
  4. echo "Usage:"
  5. echo " pip <command> [options]"
  6. echo ""
  7. echo "Commands"
  8. echo " install Install a Python package."
  9. echo ""
  10. echo "General Options:"
  11. echo " -h Show help."
  12. exit 0
  13. ;;
  14. \? )
  15. echo "Invalid Option: -$OPTARG" 1>&2
  16. exit 1
  17. ;;
  18. esac
  19. done
  20. shift $((OPTIND -1))


现在为我们的scirpt添加一个子命令install, install 把将要安装的python包作为参数

  1. > pip install urllib3

同时 install还有一个选项t,t值作为软件安装位置

  1. pip install urllib3 -t ./src/lib

我们必须要找到要执行的自命令。这是我们script的第一个实参。

  1. subcommand=$1
  2. shift # Remove `pip` from the argument list


现在我们可以处理子命令安装。在我们的示例中,选项-t实际上是在软件包参数后面的选项,因此我们首先从参数列表中删除install并处理该行的其余部分。

  1. case "$subcommand" in
  2. install)
  3. package=$1
  4. shift # Remove `install` from the argument list
  5. ;;
  6. esac


在执行shfit后,剩余的参数以 package -t src/lib形式被处理。-t选项带有一个自己的实参。这个参数被存储在 OPTARG中,我们把它保存在 targe中,以待将来使用。

  1. case "$subcommand" in
  2. install)
  3. package=$1
  4. shift # Remove `install` from the argument list
  5. while getopts ":t:" opt; do
  6. case ${opt} in
  7. t )
  8. target=$OPTARG
  9. ;;
  10. \? )
  11. echo "Invalid Option: -$OPTARG" 1>&2
  12. exit 1
  13. ;;
  14. : )
  15. echo "Invalid Option: -$OPTARG requires an argument" 1>&2
  16. exit 1
  17. ;;
  18. esac
  19. done
  20. shift $((OPTIND -1))
  21. ;;
  22. esac


把所有的代码放在一起, 我们实现了自己的pip和 install

  1. package="" # Default to empty package
  2. target="" # Default to empty target
  3. while getopts ":h" opt; do
  4. case ${opt} in
  5. h )
  6. echo "Usage:"
  7. echo " pip <command> [options]"
  8. echo ""
  9. echo "Commands"
  10. echo " install Install a Python package."
  11. echo ""
  12. echo "General Options:"
  13. echo " -h Show help."
  14. echo " -t Location where the package to install"
  15. exit 0
  16. ;;
  17. \? )
  18. echo "Invalid Option: -$OPTARG" 1>&2
  19. exit 1
  20. ;;
  21. esac
  22. done
  23. shift $((OPTIND -1)) # remove options
  24. subcommand=$1; shift # Remove 'pip' from the argument list
  25. echo "## $@"
  26. echo "## $subcommand"
  27. case "$subcommand" in
  28. # Parse options to the install sub command
  29. "install")
  30. package=$1; shift # Remove 'install' from the argument list
  31. echo "## $package"
  32. echo "## $@"
  33. # Process package options
  34. while getopts ":t:" opt; do
  35. case ${opt} in
  36. "t" )
  37. target=$OPTARG
  38. echo "install $package at $target"
  39. ;;
  40. \? )
  41. echo "Invalid Option: -$OPTARG" 1>&2
  42. exit 1
  43. ;;
  44. : )
  45. echo "Invalid Option: -$OPTARG requires an argument" 1>&2
  46. exit 1
  47. ;;
  48. esac
  49. done
  50. shift $((OPTIND -1))
  51. ;;
  52. esac


名词
OPTIND 当前处理选项的索引值
OPTARG 当前处理选项的实参值
OPTERR 当前getopts 的错误设置
options 选项
parameters 形参 【预定的参数】
argument 实参【传入的参数】

参考