引言

参考 Bash Shell中命令行选项/参数处理
在shell中处理参数的方式粗略分可以分3种:

  1. 手工处理
  2. getopts
  3. getopt

例如:

  1. ./configure.sh -u www -g www -f --with-change-source

上例中的-u就是一个选项,www就是-u这个选项的选项值,-f是一个没有选项值的选项,--with-change-source是一个长选项

在处理之前,我们先要了解几个参数

  • $0 : ./configure.sh,即命令本身,相当于C/C++中的argv[0]
  • $1 ,$2…..: $1是-u,第一个参数,$2是www第二个参数,以此类推
  • $#:参数的个数,不包括命令本身,上例中$#的值为6
  • $@:参数本身的列表,也不包括命令本身,如上例为 -u www -g www -f --with-change-source(是一个数组)
  • $*:和$@相同,但”$“ 和 “$@”(加引号)并不同,”$“将所有的参数解释成一个字符串,而”$@”是一个参数数组。

手工处理

手工处理能解决大多数的简单需求,就直接对上述的参数进行处理即可。

  1. #!/bin/bash
  2. if [ x$1 != x ]
  3. then
  4. #...有参数
  5. else
  6. then
  7. #...没有参数
  8. fi

getopts

getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由Bash内置的
可以使用man getoptsman getopt查看相关文档

getopts不支持长选项,只能使用短选项
使用getopts很简单:

  1. #configure.sh
  2. #! /bin/bash
  3. while getopt "u:g:h" opt
  4. do
  5. case $opt in
  6. u)
  7. nginx_fpm_user=${OPTARG};;
  8. g)
  9. nginx_fpm_group=${OPTARG};;
  10. h)
  11. echo "params:"
  12. echo "-h display this help and exit"
  13. echo "-u the user of nginx and php-fpm,default:_www"
  14. echo "-g the group of nginx and php-fpm,default:_www"
  15. exit 0
  16. ;;
  17. esac
  18. done

在上述例子中,opt变量就是用户输入的选项,如果getopt函数的第一个参数就是用户可以输入的选项,选项后面多一个:表示这个选项需要一个参数,每一个选项的参数存在$OPTARG中,这里还有另外一个特殊变量$OPTIND(ption index),会逐个递增,初始值为1.

现在就可以使用

  1. ./configure.sh -u _www -g _www

来调用shell脚本了。

getopt

其实重点是在这个方法,有很多细节可以说的,我上网查这个方法用了比较长的时间才勉强搞明白。

先放例子,configure2.sh如下:

  1. set -- `getopt -o hu:g: -l with-change-source,with-user:,with-group,help -- $*`
  2. while [ ! -z $1 ]
  3. do
  4. case $1 in
  5. -u|--with-user)
  6. nginx_fpm_user=$2
  7. shift
  8. shift
  9. ;;
  10. g|--with-group)
  11. nginx_fpm_group=$2
  12. shift
  13. shift
  14. ;;
  15. --with-change-source)
  16. change_source=1
  17. shift
  18. ;;
  19. h|--help)
  20. echo "params:"
  21. echo "--with-change-source set up your apt-get resource to Chinese resource"
  22. echo "-h, --help display this help and exit"
  23. echo "-u, --with-user the user of nginx and php-fpm,default:_www"
  24. echo "-g, --with-group the group of nginx and php-fpm,default:_www"
  25. exit 0
  26. ;;
  27. --)
  28. shift
  29. break
  30. ;;
  31. esac
  32. done

一条一条来说
首先命令

  1. getopt -o hu:g: -l with-change-source,with-user:,with-group,help -- $*
  1. getopt-o参数是短选项(或者也可以使用—options),和getopts很像,有一个:表示这个参数必须有参数值,两个’::’表示这个参数有可选的参数值。但是在使用的时候必须带-,比如./configure2.sh -u root
  2. -l选项(或者—long或者—longoptions)表示可以使用长选项,-l参数的参数值是一个用逗号分割的长选项组,这里是没有double dash(--)的,但是在使用的时候要加上--,例如:./configure2.sh --help
  3. 值得注意的是,如果不使用-o参数,-l参数是无法使用的,如下图(具体原因我用man getopt也没有查出来,知道的童鞋一定要告诉我)
  4. 以上命令help后面的那个--是为了让getopt知道 --后面的就是需要处理的参数列表(或者可以说是参数列表的字符串),而不是getopt自己的参数。(man bash 查看)
  1. -- A -- signals the end of options and disables further option pro-
  2. cessing. Any arguments after the -- are treated as filenames
  3. and arguments. An argument of - is equivalent to --.

image.png
然后

  1. set -- `getopt -o hu:g: -l with-change-source,with-user:,with-group,help -- $*`

把getopt命令的返回值使用·set·命令重新赋值给命令行参数(因为getopt重新排序了参数列表).getopt命令会把参数重新排序,将getopt能识别的放在--前面,把其他参数放在 --后面,所以看上面的代码在--的case当中使用了shift将参数$1出栈,后续的参数向前移位。
image.png
在上面的代码中有些地方连续调用了两次shift,是因为此时(比如使用选项-u)$1是-u,$2是-u的值,shift一次$2变成$1,所以需要shift两次

mac OS X上getopt和UbuntuCentOS上的行为可不一样呢。
image.png