bash内置的getopts工具了,用于解析shell脚本中的参数。
一、格式如下
# 代码格式参考的这篇文章:https://cloud.tencent.com/developer/article/1629932
#!/usr/bin/bash
while getopts ":a:b:h" opt_arg
do
case "$opt_arg" in
"a")
echo "参数 -a,值 $OPTARG"
;;
"b")
echo "参数 -b ,值 $OPTARG"
;;
"h")
echo "参数 -h,无值"
;;
":")
echo "-$OPTARG 选项需要指定其value值"
;;
"?")
echo "不支持 -$OPTARG 选项"
;;
*)
echo "Unknown error while processing options"
;;
esac
echo "option index is $OPTIND"
done
二、执行结果
三、参数解释
while getopts ":a:b:h",此时在 shell 界面,就可以指定给 a 或者 b 或者c 单独或者同时赋值,如while_getopt_1 -a 1 -b 2 -h 3,在 shell 中 a = 1 b = 2 c = 3
while getopts ":a:b:h" opt_arg 将 opt_arg 接收 a 或者 b 或者 c ,随后通过 case 去匹配
case "$opt_arg" in
"a")
echo "参数 -a,值 $OPTARG"
;;
"b")
3.1、其他参数解释:
-
OPTARG 表示选项值
○ case "$opt_arg" in
"a")
echo "参数 -a,值 $OPTARG"
比如匹配上了a参数,此时$OPTARG 就是参数 a 的数值,也就是sh while_getopt_1 -a 1 中的 1 -
OPTIND 表示参数索引位置
○ 输入命令 cmd -a 1。 此时命中a选项,那么OPTIND为3,表示当前在第三个参数位置。
○ 命令cmd -a 1 -b 2 。同理,当命中a之后,命中b时,OPTIND为5,表示当前在第五个参数位置。 - a后面有:,表示该选项需要参数,h后面没有:,表示不需要参数。如果是可选参数,跟 “::” 。
3.2、注意
-
这里第一个
:
表示,使用抑制错误报告模式○ 该模式在识别到无效选项时,会命中
?
,且当前选项字符会保存在OPTARG中。
○ 在识别到需要参数的选项,没有携带参数时,会命中:
,且当前选项字符会保存在OPTARG中。
逻辑参数:参数列表【abc:d:】
- copy -x
- 命中“?”, OPTARG被设置为x
- copy -c # 没有指定c的参数
- 命中 “:”,OPTARG被设置为c
-
如果输入未知参数,比如 -d 在shell 中没有定义,会匹配 "?") 或者 *) ,具体匹配谁看谁在第一位
-
参数中出现正确、和未知的参数,比如 sh while_getopt_1 -a 1 -d 3 ,其中 -d 是未知参数,那 -a 执行还是不执行,回答是-a 是执行的,shell 是按照循序执行的,故 -d 3 之前没发生错误,-a 执行,但如果是 -d 3 -a 1 ,则一开始就未知,后续 while getopts 都不会执行,但 while getopts 外还是会执行,除非在 while getopts 中增加 exit 1 或者 break
[root@master02 bash_scritp]# cat while_getopt_1
#!/bin/bash
yanzheng1 (){
echo "yanzheng1"
}
yanzheng (){
echo "yanzheng"
}
while getopts :a:b:h: opt_arg
do
case $opt_arg in
a)
echo "参数 -a,值 $OPTARG"
REDIS_NODES=(${OPTARG//;/ })
yanzheng;
;;
b)
echo "参数 -b ,值 $OPTARG"
;;
h)
echo "参数 -h,无值"
;;
*)
echo "Unknown error while processing options 111"
;;
:)
echo "-$OPTARG 选项需要指定其value值"
;;
?)
echo "不支持 -$OPTARG 选项"
;;
esac
echo "option index is $OPTIND"
done
echo "nihao"
[root@master02 bash_scritp]# sh while_getopt_1 -a 1
参数 -a,值 1
yanzheng
option index is 3
nihao
yanzheng1
[root@master02 bash_scritp]# sh while_getopt_1 -b 2
参数 -b ,值 2
option index is 3
nihao
yanzheng1
[root@master02 bash_scritp]# sh while_getopt_1 -d 3 -a 1
Unknown error while processing options 111
option index is 2
nihao
yanzheng1
[root@master02 bash_scritp]# sh while_getopt_1 -a 1 -d 3
参数 -a,值 1
yanzheng
option index is 3
Unknown error while processing options 111
option index is 4
nihao
yanzheng1
[root@master02 bash_scritp]#
-
遇到未知参数的后,立马退出,不进行执行整个shell
exit 1
[root@master02 bash_scritp]# cat while_getopt_1
#!/bin/bash
yanzheng1 (){
echo "yanzheng1"
}
yanzheng (){
echo "yanzheng"
}
while getopts :a:b:h: opt_arg
do
case $opt_arg in
a)
echo "参数 -a,值 $OPTARG"
REDIS_NODES=(${OPTARG//;/ })
yanzheng;
;;
b)
echo "参数 -b ,值 $OPTARG"
;;
h)
echo "参数 -h,无值"
;;
*)
echo "Unknown error while processing options 111"
exit 1
;;
:)
echo "-$OPTARG 选项需要指定其value值"
;;
?)
echo "不支持 -$OPTARG 选项"
;;
esac
echo "option index is $OPTIND"
done
echo "nihao"
yanzheng1;
[root@master02 bash_scritp]# sh while_getopt_1 -d 3 -a 1
Unknown error while processing options 111
[root@master02 bash_scritp]#
四、程序演示
1.正常输出所有值:
2.不带选项。注意这里不带选项是检测不到的。可以利用这个特性,实现省略选项直接使用命令的功能。
3.不在参数列表的选项,命中“?”
4.需要参数的选项没有带参数,命中“:”
除此之外,还可以使用长参数。跟高级的用法可以参考此文章:https://www.cnblogs.com/f-ck-need-u/p/9758075.html
五、实战演练
写一个从安卓手机上传文件到pc上的脚本。代码参考如下:
#!/bin/bash
srcPath=/sdcard/DCIM/Camera/
srcFile=$(adb shell ls -ltr $srcPath | tail -n 1 | tr ' ' '\n' | tail -n 1 ) # adb可以轻松的执行Linux Shell命令,如adb shell dir 就是列举目录,在Linux中根目录为/而不是Windows上的C盘、D盘。具体看 https://blog.csdn.net/Jerry00713/article/details/129775730
dstPath=$(pwd)/
dstFile=
bool=false # 是否使用了opt参数。没使用就走默认流程
is_opt_r=false # 是否开启递归,这表示拷贝的可能是个目录文件
# 输出提示内容的字符串
Helper(){
echo "使用说明:"
echo " -f 指定源文件名(安卓/sdcard/DICM/目录下)"
echo " -F 指定源文件的路径(不能与-f 一起使用)"
echo " -o 指定存放的文件名(PC当前脚本所在目录下)"
echo " -O 指定存放文件的路径(不能与-O 一起使用)"
echo -e "\n示例参考:"
echo -e " ./copy \t\t\t拷贝 /sdcard/DICM/ 目录下最新生成的文件 到当前脚本所在目录 "
echo -e " ./copy -f xxx.png \t\t拷贝 /sdcard/DICM/xxx.png 文件 到当前脚本所在目录 "
echo -e " ./copy -F /aaa/bbb/xxx.png \t拷贝 /aaa/bbb/xxx.png 文件 到当前脚本所在目录 "
echo -e " ./copy -o file \t\t拷贝 /sdcard/DICM/ 目录下最新生成的文件 到当前脚本所在目录【命名为file】 "
echo -e " ./copy -O /ccc/ddd/file \t拷贝 /sdcard/DICM/ 目录下最新生成的文件 到当前脚本所在目录"
echo -e "\n建议使用以下两种格式:"
echo " eg1: ./copy -f 1.png -o file_1.png"
echo " eg2: ./copy -F sdcard/DCIM/1.png -O /Users/rt/Desktop/file_1.png"
}
Param_error(){ # 参数互斥
echo "参数错误:请检查参数列表"
echo " -f 与 -F 不能同时使用。"
echo " -o 与 -O 不能同时使用。"
exit
}
# f与F互斥,不能同时用
or_op_f=`echo "$*" | grep -Eo '\-f | \-F ' | wc -l`
[ "$or_op_f" -eq 2 ] && Param_error
# o与O互斥,不能同时用
or_op_o=`echo "$*" | grep -Eo '\-o | \-O ' | wc -l`
[ "$or_op_o" -eq 2 ] && Param_error
while getopts ":f:F:o:O:rh" opt_arg
do
bool=true
case "$opt_arg" in
"f")
srcFile=$OPTARG
#echo "set | srcFile name : $srcFile"
;;
"F")
srcPath=""
srcFile=$OPTARG
#echo "set | srcPath/srcFile name : $srcFile"
;;
"o")
dstFile=$OPTARG
#echo "set | dstFile name : $dstFile"
;;
"O")
dstPath=""
dstFile=$OPTARG
#echo "set | dstPath/dstFile name : $dstFile"
;;
"r")
is_opt_r=true
;;
"h")
Helper && exit
;;
":")
echo "请添加$OPTARG的参数"
;;
"?")
echo "参数错误!!"
;;
*)
echo "Unknown error while processing options"
;;
esac
done
if [ "$bool" != "true" ]; then
if [ -n "$1" ]; then
secFile=$1
if [ -n "$2" ]; then
dstFile=$2
fi
fi
fi
# 检查安卓端源文件是否为目录
is_dir="$(adb shell [ -d "$srcPath$srcFile" ] && echo "true")"
# 源文件是否为目录
if [ "$is_dir" == "true" ] && [ "$is_opt_r" == "false" ]; then
echo "error: $fsrcPath$srcFile 是一个目录"
echo " 对目录操作,请使用 -r 参数,进行递归拷贝"
exit
fi
echo "adb pull $srcPath$srcFile $dstPath$dstFile"
adb pull $srcPath$srcFile $dstPath$dstFile
执行:
需要注意的是,如果源文件是一个目录我们应该怎样操作。(这里提供了 -r 参数,用户手动确认会对目录进行拷贝时,才进行该操作)