这是本人第一次写博客,之前从一些开源技术网站上看到不少大牛和前辈们的文章,从中学习受益。本着开源界的奉献和学习精神,觉得有必要将自己的学习成果拿出来与大家一起交流分享,既当作是一种自我学习的总结也可能帮助到有需要的人,因此发表了该篇博客。本文主要讲述在shell中getopts命令的使用以及本人写的一个脚本小案例。由于本人技术水平和能力有限,文章中的命令说明部分主要来自于help getopts的翻译,同时会带有一些本人的主观意识,如有任何描述不对的地方,请大家批评指正。
在日常工作中写shell脚本,往往会涉及到一些命令行选项和参数的解析,这个解析过程如果完全由自己写代码去控制实现将会有一定困难,这里给大家介绍一个命令解析工具getopts。getopts是bash shell的内建命令,作用是在shell脚本中解析由命令行传递的选项和参数(也可能传递给函数、另一个被调用的shell脚本的位置参数,关于选项或参数,后面会讲解)。getopts只支持短选项,若要解析长选项请参考getopt。
getopts optstring name [arg]
相关的术语:
选项(option):GNU风格的命令选项,如:-x,-y等减号加上单个字母的为短选项;--help为长选项;
选项的参数:某些选项之后必须尾随参数,如:-f xxx.conf,xxx.conf称为选项的参数,但-f和xxx.conf之间的空格不是必须的;
参数:位于最后一个选项,空格之后的字符串;
optionstring:用于匹配option的字符串,optionstring中每个字母对应的是去除减号后的option(这里指短选项);
[arg]:用来替代位置参数被getopts解析的,类似于将$@赋值给arg;
OPTIND: getopts的一个内置变量,表示下一个要处理的参数索引,每次shell或shell脚本执行的时候初始化为1;
name: getopts每解析到一个选项,都会将该选项对应的字符复制给name变量.
getopts命令的简单解释:
1.optstring中如果字母后面跟随着冒号,表示该字母所表示的option后面是带参数的,参数值将被赋到OPTARG变量中。如test.sh中包含:getopts "i:" name; 那么在命令行执行test.sh -i 1.1.1.1时,$name='i',$OPTARG=1.1.1.1
getopts包含两种错误处理的模式
安静模式下:
2.optstring的第一个字符为冒号时,getopts将使用安静模式来报告错误(但不会有错误提示输出),生产环境中一般使用该模式;
3.如果输入一个无效的字符作为选项时(optstring中没包含的字符),将‘?’赋值给name变量,该无效选项的字符赋值给OPTARG变量;
4.如果选项要求跟随的参数没找到,getopts会将冒号":"赋值给name变量,设置OPTARG为发现的选项字符;
非安静模式下:
5.如果getopts不处于安静模式,一个无效的选项被发现,'?'将被设置到name变量中且unset OPTARG;
6.如果选项要求的参数没找到,'?'将被设置到name,OPTARG将被unset,错误提示信息将被打印.
退出状态:
7.如果shell中$OPTERR环境变量值为0,getopts将关闭错误信息的打印,即使optstring中第一个字符不是冒号。默认的$OPTERR值为1;
8.getopts一般情况下解析位置参数($0-$9),但位置参数超过这个范围时仍然能被正确解析;
9.如果选项被发现(匹配optionstring)返回true,如果遇到了选项的结尾或者有错误则返回false.
1. 不带可选参数[arg]
#!/bin/bash echo "OPTIND starts at $OPTIND" while getopts ":q:p" optname do case "$optname" in "p") echo "Option $optname is specified" ;; "q") echo "Option $optname has value $OPTARG" #shift 1 shift $((OPTIND-2)) ;; "?") echo "Unknown option $OPTARG" ;; ":") echo "No argument value for option $OPTARG" ;; *) # Should not occur echo "Unknown error while processing options" ;; esac echo "OPTIND is now $OPTIND" done
执行结果:
[root@dns1 ~]# bash test_getopts.sh -q aa -p -h OPTIND starts at 1 Option q has value aa OPTIND is now 3 Unknown option h OPTIND is now 4
2. 带可选参数[arg]
#!/bin/bash echo "OPTIND starts at $OPTIND" while getopts ":q:p" optname "-q qarg" "-p" "-h" do case "$optname" in "p") echo "Option $optname is specified" ;; "q") echo "Option $optname has value $OPTARG" ;; "?") echo "Unknown option $OPTARG" ;; ":") echo "No argument value for option $OPTARG" ;; *) # Should not occur echo "Unknown error while processing options" ;; esac echo "OPTIND is now $OPTIND" done
执行结果:
[root@dns1 ~]# bash test_getopts.sh OPTIND starts at 1 Option q has value qarg OPTIND is now 2 Option p is specified OPTIND is now 3 Unknown option h OPTIND is now 4
3. 实现一个添加yum源的脚本
#!/bin/bash # # Descrition: this script will download the yum repo file from mirrors.163.com and epel for CENTOS/REDHAT DISTIBUTE # Author: Gateray Jo # mail: [email protected] set -e prog=`basename $0` arch=`uname -m` release=`lsb_release -r | sed 's/.*:[[:space:]]*//'` function usage() { cat<<eof Usage: ./${prog} [options] [arg] -h: print the command help. -u: specified the baseurl for yum repository, must use with -ufn combination. -f: specified the name of local repofile(no ".repo" ext name) which place in /etc/yum.repos.d/, must use with -ufn combination. -n: specified the repo's name in xxx.repo file, must use with -ufn combination. -d: specified the url of repofile which will be download to /etc/yum.repos.d/. By default, nothing options are specified which will download epel and 163 yum repofile. eof } function default() { major_rel=${release%%.*} # download 163 repofile printf "Download 163 repofile from mirrors.163.com:\n" wget -P /etc/yum.repos.d http://mirrors.163.com/.help/CentOS${major_rel}-Base-163.repo printf "Download epel repofile from http://dl.fedoraproject.org.:\n" if [[ $arch = "x86_64" ]]; then [ ${major_rel} -eq 5 ] && rpm -ivh http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm [ ${major_rel} -eq 6 ] && rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm else [ ${major_rel} -eq 5 ] && rpm -ivh http://dl.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm [ ${major_rel} -eq 6 ] && rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm fi return $? } if [[ $# -eq 0 ]]; then default exit $? fi while getopts ":hu:f:n:d:" optname; do case $optname in "h") usage && exit 0 ;; "u") u_opt=1 u_arg=$OPTARG ;; "f") f_opt=1 u_arg=$OPTARG ;; "n") n_opt=1 n_arg=$OPTARG ;; "d") wget -P /etc/yum.repos.d $OPTARG exit $? ;; "?") echo "Unknown option $OPTARG" usage && exit 1 ;; ":") echo "It is need a option value for $OPTARG" usage && exit 1 ;; *) echo 'Unknown error!' && exit 1 ;; esac done if [[ $u_opt -eq 1 ]] && [[ $f_opt -eq 1 ]] && [[ $n_opt -eq 1 ]]; then if [[ ${n_arg} =~ '-' ]]; then echo "Invalid value for -n option, \"${n_arg}\" is include '-' character." exit 1 fi cat >> /etc/yum.repos.d/${f_arg}.repo <<eof [${n_arg}] name=${n_arg} baseurl=${u_arg} enabled=1 gpgcheck=0 eof else usage && exit 1 fi
脚本的执行:
1)打印命令帮助
[root@dns1 ~]# ./load_yum_repo.sh -h Usage: ./load_yum_repo.sh [options] [arg] -h: print the command help. -u: specified the baseurl for yum repository, must use with -ufn combination. -f: specified the name of local repofile(no ".repo" ext name) which place in /etc/yum.repos.d/, must use with -ufn combination. -n: specified the repo's name in xxx.repo file, must use with -ufn combination. -d: specified the url of repofile which will be download to /etc/yum.repos.d/. By default, nothing options are specified which will download epel and 163 yum repofile.
2)不带任何选项参数时,自动下载163和epel的yum源repofile
[root@dns1 ~]# ./load_yum_repo.sh Download 163 repofile from mirrors.163.com: --2015-04-04 22:23:05-- http://mirrors.163.com/.help/CentOS6-Base-163.repo Resolving mirrors.163.com... 123.58.173.106 Connecting to mirrors.163.com|123.58.173.106|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 2006 (2.0K) [application/octet-stream] Saving to: “/etc/yum.repos.d/CentOS6-Base-163.repo” 100%[==========================================================================================>] 2,006 --.-K/s in 0.05s 2015-04-04 22:23:05 (39.2 KB/s) - “/etc/yum.repos.d/CentOS6-Base-163.repo” saved [2006/2006] Download epel repofile from http://dl.fedoraproject.org.: Retrieving http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm warning: /var/tmp/rpm-tmp.wVrCDS: Header V3 RSA/SHA256 Signature, key ID 0608b895: NOKEY Preparing... ########################################### [100%] 1:epel-release ########################################### [100%]
3)从指定的url中下载repofile
[root@dns1 ~]# ./load_yum_repo.sh -d http://mirrors.163.com/.help/CentOS6-Base-163.repo --2015-04-04 22:13:47-- http://mirrors.163.com/.help/CentOS6-Base-163.repo Resolving mirrors.163.com... 123.58.173.106 Connecting to mirrors.163.com|123.58.173.106|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 2006 (2.0K) [application/octet-stream] Saving to: “/etc/yum.repos.d/CentOS6-Base-163.repo” 100%[==========================================================================================>] 2,006 --.-K/s in 0.05s 2015-04-04 22:13:47 (40.9 KB/s) - “/etc/yum.repos.d/CentOS6-Base-163.repo” saved [2006/2006]
4)创建自定义的repofile
[root@dns1 ~]# ./load_yum_repo.sh -f test -n test -u file:///mnt [root@dns1 ~]# cat /etc/yum.repos.d/test.repo [test] name=test baseurl=file:///mnt enabled=1 gpgcheck=0