免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 45323 | 回复: 15
打印 上一主题 下一主题

shell基础学习第二十篇-向脚本传递参数 [复制链接]

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-11-26 14:05 |只看该作者 |倒序浏览
前面已经讲到如何使用特定变量$ 1 . . $ 9向脚本传递参数。$ #用于统计传递参数的个数。可
以创建一个u s a g e语句,需要时可通知用户怎样以适当的调用参数调用脚本或函数。
本章内容有:
• shift。
• getopts。
• shift和g e t o p t s例子。

简单地说,下述脚本框架控制参数开始与停止。脚本需要两个参数,如果没有输入两个
参数,那么产生一个u s a g e语句。注意这里使用c a s e语句处理输入脚本的不同参数。

  1. #!/bin/bash
  2. # opt.sh
  3. usage()
  4. {
  5.         echo "usage: `basename $0` start|stop process name"
  6. }      
  7. OPT=$1
  8. PROCESSID=$1
  9. if [ $# -ne 2 ]; then
  10.         usage
  11.         exit 1
  12. fi      
  13. case $OPT in
  14.         start|Start) echo "Starting..$PROCESSID"
  15.         # some process to go here
  16.         ;;
  17.         stop|Stop) echo "Stopping..$PROCESSID"
  18.         # some process to go here
  19.         ;;
  20.         *)usage
  21.         ;;
  22. esac   
复制代码
执行脚本,输入一下参数,结果为:
  1. [root@localhost ~]# sh opt.sh start named
  2. Starting..start
  3. [root@localhost ~]# sh opt.sh start
  4. usage: opt.sh start|stop process name
复制代码
任何U N I X或L I N U X命令均接受一般格式:
命令选项文件选项部分最多可包含1 2个不同的值。上述脚本中,如果必须控制不同的命令选项,就要
加入大量脚本。这里只控制两个选项:开始和停止。
幸运的是s h e l l提供s h i f t命令以帮助偏移选项,使用s h i f t可以去除只使用$ 1到$ 9传递参数的
限制。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
2 [报告]
发表于 2010-11-26 14:10 |只看该作者
20.1 shift命令
向脚本传递参数时,有时需要将每一个参数偏移以处理选项,这就是s h i f t命令的功能。
它每次将参数位置向左偏移一位,下面用一段简单脚本详述其功能。脚本使用w h i l e循环反馈
所有传递到脚本的参数。
  1. #!/bin/bash
  2. # opt2.sh
  3. loop=0
  4. while [ $# -ne 0 ] # while there are still arguments
  5. do
  6.         echo $1
  7. done
复制代码
你可能想像,上述脚本一直执行,直到命令行中不再有更多的参数输入。错了,因为没
有办法偏移到脚本中下一个参数,将只会反馈出第一个参数。执行结果如下:
  1. # sh opt2.sh file1 file2 file3
  2. file1
  3. file1
  4. file1
  5. ......
复制代码
20.1.1 shift命令简单用法
使用s h i f t命令来处理传递到脚本的每一个参数。改动后脚本如下:
  1. #!/bin/bash
  2. # opt2.sh
  3. loop=0
  4. while [ $# -ne 0 ] # while there are still arguments
  5. do
  6.         echo $1
  7.         shift
  8. done
复制代码
现在执行一下,结果就会明显不同了。如下所示:
  1. [root@localhost ~]# sh opt2.sh file1 file2 file3
  2. file1
  3. file2
  4. file3
复制代码
20.1.2 命令行输入的最后一个参数
虽然还没有讲e v a l命令,如果需要知道命令行中输入的最后一个参数(通常是一个文件名),
可以有两种选择:使用命令eval echo \$$#;使用s h i f t命令:shift 'expr $# -2'。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
3 [报告]
发表于 2010-11-26 14:42 |只看该作者
20.1.3 使用shift处理文件转换
s h i f t可使控制命令行选项更加容易。下面构造一个转换脚本,使用t r将文件名转换为大写
或小写。
脚本选项为:
-l 用于小写转换。
-u 用于大写转换。
使用s h i f t命令将脚本放在一起以控制- l和- u选项。脚本的第一版本如下:
  1. #!/bin/bash
  2. # tr_case.sh
  3. # case conversion
  4. usage()
  5. {
  6.         # usage
  7.         echo "usage: `basename $0` -[l|u] file [files]" >&2
  8.         exit 1
  9. }      
  10. if [ $# -eq 0 ]; then
  11.         # no parameters passed !
  12. fi      
  13. while [ $# -gt 0 ]
  14. do
  15.         case $1 in
  16.                 -u|-U) echo "-u option specified"
  17.                 # do any setting of variables here for lowercase then shift
  18.                 shift
  19.                 ;;
  20.                 -l|-L) echo "-l option specified"
  21.                 # do any setting of variables here for uppercase then shift
  22.                 shift
  23.                 ;;
  24.                 *) usage
  25.                 ;;
  26.         esac   
  27. done   
复制代码
首先检查脚本是否有参数,如果没有,打印u s a g e语句,如果有需要处理的参数,使用
c a s e语句捕获每一个传送过来的选项。当处理完此选项后,使用s h i f t命令搜集命令行中下一选
项,如果未发现匹配选项,打印u s a g e语句。
当向脚本传递两个无效参数时,输出如下:
  1. [root@localhost ~]# sh tr_case.sh -u -l -k
  2. -u option specified
  3. -l option specified
  4. usage: tr_case.sh -[l|u] file [files]
复制代码
下一步就是要用c a s e语句处理选项后传递过来的文件名。为此需改动c a s e语句。c a s e语句
中捕获任意模式*应该为- *以允许传递无效选项,例如- p或- q。
*模式也匹配传递过来的所有文件名,以便用f o r循环处理每一个文件,这里也将使用- f选
项检测文件是否存在。
改动后的c a s e语句如下:
  1. case
  2.           ......
  3.                 -*) echo "usage: `basename $0` -[l|u] file [file..]"
  4.                 exit 1
  5.                 ;;
  6.                 *) # collect the files to process
  7.                 if [ -f $1 ]; then
  8.                         # add the filenames to a variable list
  9.                         FILES=$FILES" "$1
  10.                 else   
  11.                         echo "`basename $0`: Error cannot find the file $1"
  12.                 fi      
  13.                 shift
  14.                 ;;
  15.         esac
复制代码
还需要指定与选项( - l,- u)相关的变量设置。这些变量是:
T R C A S E 保存转换类型(大写或小写)。
E X T 所有文件转换后,大写文件名为. U C,小写为. L C,不保存初始文件状态。
O P T 如果给出此选项,设其为y e s,否则为n o。如果没有给出此选项,捕获此信息并反
馈出来。
其他部分脚本用于实际转换处理,这里即t r命令。t r命令放在c a s e语句f o r循环中读取文件
名进行处理的脚本末尾部分。
以下为完整脚本:
  1. #!/bin/bash
  2. # tr_case.sh
  3. # case conversion
  4. # convert files to either upper or lower case
  5. FILES=""
  6. TRCASE=""
  7. EXT=""
  8. OPT=no
  9. # gets called when a conversion fails
  10.                 # do any setting of variables here for lowercase then shift
  11.                 shift
  12.                 ;;
  13. error_msg()
  14. {
  15.         _FILENAME=$1
  16.                
  17.                 shift
  18.                 ;;
  19.         echo "`basename $0`: Error the conversion failed on $_FILENAME"
  20. }

  21. if [ $# -eq 0 ]; then
  22.         usage
  23.         # no parameters passed !
  24. fi
  25. while [ $# -gt 0 ]
  26. do
  27.         case $1 in
  28.                 # set the variables based on what option was used
  29.                 -u) TRCASE=upper
  30.                 EXIT".UC"
  31.                 OPT=yes
  32.                 shift
  33.                 ;;
  34.                 -l) TRCASE=lower
  35.                 EXT=".LC"
  36.                 OPT=yes
  37.                 shift
  38.                 ;;
  39.                 -help) echo "convert a file(s) to uppercase from lowercase"
  40.                 echo "convert a file(s) from lowercase to uppercase"
  41.                 echo "will convert all characters according to the"

  42.                 ;;
  43.                 echo "  specified command option."
  44.                 echo " Where option is"
  45.                 echo " -l Convert to lowercase"
  46.                 echo " -u Convert to uppercase"
  47.                 echo " The original file(s) is not touched. A new file(s)"
  48.                 echo "will be created with either a .UC or .LC extension"
  49.                 echo "usage: $0 -[l|u] file [file..]"
  50.                 exit 0
  51.                 ;;
  52.                 -*) echo "usage: `basename $0` -[l|u] file [file..]"
  53.                 exit 1
  54.                 ;;
  55.                 *) # collect the files to process
  56.                 if [ -f $1 ]; then
  57.                         # add the filenames to a variable list
  58.                         FILES=$FILES" "$1
  59.                 else   
  60.                         echo "`basename $0`: Error cannot find the file $1"
  61.                 fi      
  62.                 shift
  63.                 ;;
  64.         esac
  65. done
  66. # no options given ... help the user
  67. if [ "$OPT" == "no" ]; then
  68.         echo " try `basename $0` --help"
  69.         exit 1
  70. fi      
  71. # now read in all the file(s)
  72. # use the variable LOOP, I just love the word LOOP
  73. for LOOP in $FILES
  74. do      
  75.         case $TRCASE in
  76.                 lower) cat $LOOP | tr "[a-z]" "[A-Z]" > $LOOP$EXT
  77.                 if [ $? != 0 ]; then
  78.                         error_msg $LOOP
  79.                 else   
  80.                         echo "Converted file called $LOOP$EXT"
  81.                 fi      
  82.                 ;;
  83.                 upper) cat $LOOP | tr "[A-Z]" "[a-z]" >$LOOP$EXT
  84.                 if [ $? !=0 ]; then
  85.                         error_msg $LOOP
  86.                 else   
  87.                         echo "Converted file called $LOOP$EXT"
  88.                 fi
  89.                 ;;
  90.         esac
  91. done   
复制代码
执行上述脚本,给出不同选项,得结果如下:
转换一个不存在的文件:
  1. [root@localhost ~]# sh tr_case.sh -k cursor
  2. usage: tr_case.sh -[l|u] file [file..]
复制代码
传递不正确选项:
  1. [root@localhost ~]# sh tr_case.sh cursor
  2. tr_case.sh: Error cannot find the file cursor
  3. tr_case.sh : Error you need to specify an option. No action taken
  4. try tr_case.sh --help
复制代码
只键入文件名,希望脚本提示更多帮助信息:
  1. [root@localhost ~]# sh tr_case.sh
  2. For more info try tr_case.sh --help
复制代码
输入两个有效文件及第三个无效文件:
  1. [root@localhost ~]# sh tr_case.sh -l file1 file2 sd
  2. tr_case.sh: Error cannot find the file sd
  3. Converted file called file1.LC
  4. Converted file called file2.LC
复制代码
使用上述脚本可以将许多文件转换为同样的格式。编写一段脚本,使其控制不同的命令
行选项,这种方式编程量很大,是一件令人头疼的事。
假定要写一段脚本,要求控制以下各种不同的命令行选项:
命令-l -c 23 -v 文件1文件2
s h i f t命令显得力不从心,这就需要用到g e t o p t s命令。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
4 [报告]
发表于 2010-11-26 14:48 |只看该作者
20.2 getopts

g e t o p t s可以编写脚本,使控制多个命令行参数更加容易。g e t o p t s用于形成命令行处理标
准形式。原则上讲,脚本应具有确认带有多个选项的命令文件标准格式的能力。

20.2.1 getopts脚本实例
通过例子可以更好地理解g e t o p t s。以下g e t o p t s脚本接受下列选项或参数。
• a 设置变量A L L为t r u e。
• h 设置变量H E L P为t r u e。
第20章向脚本传递参数229
下载
• f 设置变量F I L E为t r u e。
• v 设置变量V E R B O S E为t r u e。
对于所有变量设置,一般总假定其初始状态为f a l s e:
  1. #!/bin/bash
  2. # getopt1.sh
  3. # set the vars
  4. ALL=false
  5. HELP=false
  6. FILE=false
  7. VERBOSE=false
  8. while getopts ahfgv OPTION
  9. do
  10.         case $OPTION in
  11.                 a) ALL=true
  12.                 echo "ALL is $ALL"
  13.                 ;;
  14.                 h) HELP=true
  15.                 echo "HELP is $HELP"
  16.                 ;;
  17.                 f) FILE=true
  18.                 echo "FILE is $FILE"
  19.                 ;;
  20.                 v) VERBOSE=true
  21.                 echo "VERBOSE is $VERBOSE"
  22.                 ;;
  23.         esac   
  24. done   
复制代码
g e t o p t s一般格式为:
getopts option_string variable
在上述例子中使用脚本:
while getopts ahfgv OPTION
可以看出w h i l e循环用于读取命令行,o p t i o n s t r i n g为指定的5个选项(- a,- h,- f,- g,- v),
脚本中v a r i a b l e为O P T I O N。注意这里并没有用连字符指定每一单个选项。
运行上述脚本,给出几个有效和无效的选项,结果为:
  1. [root@localhost ~]# sh getopt1.sh -a -h
  2. ALL is true
  3. HELP is true
  4. [root@localhost ~]# sh getopt1.sh -a -h -p
  5. ALL is true
  6. HELP is true
  7. getopt1.sh: illegal option -- p
复制代码
可以看出不同选项的结合方式。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
5 [报告]
发表于 2010-11-26 14:49 |只看该作者
20.2.2 getopts使用方式

g e t o p t s读取o p t i o n s t r i n g,获知脚本中使用了有效选项。
g e t o p t s查看所有以连字符开头的参数,将其视为选项,如果输入选项,将把这与
o p t i o n s t r i n g对比,如果匹配发现,变量设置为O P T I O N,如果未发现匹配字符,变量能够设
置为?。重复此处理过程直到选项输入完毕。
g e t o p t s接收完所有参数后,返回非零状态,意即参数传递成功,变量O P T I O N保存最后处
理参数,一会儿就可以看出处理过程中这样做的好处。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
6 [报告]
发表于 2010-11-26 15:02 |只看该作者
20.2.3 使用getopts指定变量取值

有时有必要在脚本中指定命令行选项取值。g e t o p t s 为此提供了一种方式,即在
o p t i o n s t r i n g中将一个冒号放在选项后。例如:
getopts ahfvc: OPTION
上面一行脚本指出,选项a、h、f、v可以不加实际值进行传递,而选项c必须取值。使用
选项取值时,必须使用变量O P TA R G保存该值。如果试图不取值传递此选项,会返回一个错
误信息。错误信息提示并不明确,因此可以用自己的反馈信息屏蔽它,方法如下:
将冒号放在o p t i o n s t r i n g开始部分。
while getopts :ahfgvc: OPTION
在c a s e语句里使用?创建一可用语句捕获错误。
  1. #!/bin/bash
  2. # getopt2.sh
  3. # set the vars
  4. ALL=false
  5. HELP=false
  6. FILE=false
  7. VERBOSE=false
  8. COPIES=0
  9. # the value for the -c option is set to zero
  10. while getopts ahfgvc: OPTION
  11. do
  12.         case $OPTION in
  13.                 a) ALL=true
  14.                 echo "ALL is $ALL"
  15.                 ;;
  16.                 h) HELP=true
  17.                 echo "HELP is $HELP"
  18.                 ;;
  19.                 f) FILE=true
  20.                 echo "FILE is $FILE"
  21.                 ;;
  22.                 v) VERBOSE=true
  23.                 echo "VERBOSE is $VERBOSE"
  24.                 ;;
  25.                 c) COPIES=$OPTARG
  26.                 echo "COPIES is $COPIES"
  27.                 \?) # usage statement
  28.                 echo "`basename $0` -[a h f v] -[c value] file" >&2
  29.                 ;;
  30.         esac   
  31. done
复制代码
运行上述脚本,选项- c不赋值,将返回错误,但显示的是脚本语句中的反馈信息:
  1. [root@localhost ~]# sh getopt2.sh -ah -c
  2. ALL is true
  3. HELP is true
  4. getopt2.sh: option requires an argument -- c
  5. getopt2.sh -[a h f v] -[c value] file
复制代码
现在输入所有的合法选项:
  1. [root@localhost ~]# sh getopt2.sh -ah -c 3
  2. ALL is true
  3. HELP is true
  4. COPIES is 3
复制代码

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
7 [报告]
发表于 2010-11-26 15:09 |只看该作者
20.2.4 访问取值方式

g e t o p t s的一种功能是运行后台脚本。这样可以使用户加入选项,指定不同的磁带设备以
备份数据。使用g e t o p t s实现此任务的基本框架如下:
  1. #!/bin/bash
  2. # backups.sh
  3. QUITE=n
  4. DEVICE=awa
  5. LOGFILE=/tmp/logbackup
  6. usage()
  7. {
  8.         echo "Usage: `basename $0` -d [device] -l [logfile] -q"
  9.         exit 1
  10. }
  11. if [ $# == 0 ]; then
  12.         usage
  13. fi
  14. while getopts :qd:l: OPTION
  15. do
  16.         case $OPTION in
  17.                 q) QUITE=y
  18.                 LOGFILE="/tmp/backup.log"
  19.                 ;;
  20.                 d) DEVICE=$OPTARG
  21.                 ;;
  22.                 l) LOGFILE=$OPTARG
  23.                 ;;
  24.                 \?) usage
  25.                 ;;
  26.         esac
  27. done
  28. echo "you chose the following options .. I can process these"
  29. echo "Quite= $QUITE $DEVICE $LOGFILE"
复制代码
上述脚本中如果指定选项d,则需为其赋值。该值为磁带设备路径。用户也可以指定是否
备份输出到登录文件中的内容。运行上述脚本,指定下列输入:
  1. [root@localhost ~]# sh backups.sh -d/dev/rmt0 -q
  2. you chose the following options .. I can process these
  3. Quite= y /dev/rmt0 /tmp/backup.log
复制代码
g e t o p t s检查完之后,变量O P TA R G取值可用来进行任何正常的处理过程。当然,如果输
入选项,怎样进行进一步处理及使该选项有有效值,完全取决于用户。
以上是使用g e t o p t s对命令行参数处理的基本框架。
实际处理文件时,使用f o r循环,就像在t r- c a s e脚本中使用s h i f t命令过滤所有选项一样。
使用g e t o p t s与使用s h i f t方法比较起来,会减少大量的编程工作。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
8 [报告]
发表于 2010-11-26 15:24 |只看该作者
20.2.5 使用getopts处理文件转换

现在用所学知识将t r- c a s e脚本转换为g e t o p t s版本。命令行选项g e t o p t s方法与s h i f t方法的唯
一区别是一个V E R B O S E选项。
变量V E R B O S E缺省取值为n o,但选择了命令行选项后, c a s e语句将捕获它,并将其设为
y e s,反馈的命令是一个简单的i f语句。
  1. if [ "$VERBOSE" == "on" ]; then
  2.                         echo "doing..lower on $LOOP .. newfile called $LOOP$EXT"
  3.                 fi   
复制代码
如果正在使用其他系统命令包,它总是反馈用户动作,只需简单地将包含错误的输出重
定向到/ d e v / n u l l中即可。如:
命令>/dev/null 2 >&1
缺省时V E R B O S E关闭(即不显示),使用- v选项可将其打开。例如要用V E R B O S E将
m y f i l e文件系列转换为小写,方法如下:
tr-case -l -v myfile1 myfile2 ...
或者
tr-case -v -l myfile1 myfile2 ...
可能首先注意的是使用g e t o p t s后脚本的缩减效果。这里用于文件处理的脚本与s h i f t版本
相同。
脚本如下:
  1. #!/bin/bash
  2. # tr_case2.sh
  3. # convert case, using getopts
  4. EXT=""
  5. TRCASE=""
  6. FLAG=""
  7. OPT="no"
  8. VERBOSE="off"
  9. while getopts :luv OPTION
  10. do
  11.         case $OPTION in
  12.                 l) TRCASE="lower"
  13.                 EXT=".LC"
  14.                 OPT=yes
  15.                 ;;
  16.                 u) TRCASE="upper"
  17.                 EXT=".UC"
  18.                 OPT=yes
  19.                 ;;
  20.                 v) VERBOSE=on
  21.                 ;;
  22.                 \?) echo "usage: `basename $0`: -[l|u] --v file[s]"
  23.                         echo "doing.. lower on $LOOP .. newfile called $LOOP$EXT
  24.                 exit 1
  25.                 ;;
  26.         esac   
  27. done   
  28. # next argument down only please
  29. shift `expr $OPTION - 1`
  30. # are there any argument passed ??
  31. if [ "$#" == "0" ] || [ "$OPT" == "no" ]; then
  32.         echo "usage: `basename $0` : -[l|u] -v file[s]" >&2
  33.         exit 1
  34. fi      
  35. for LOOP in "$@"
  36. do      
  37.         if [ ! -f $LOOP ]; then
  38.                 echo "`basename $0` : Error cannot find file $LOOP" >&2
  39.                 exit 1
  40.         fi      
  41.         echo $TRCASE $LOOP
  42.         case $TRCSSE in
  43.                 lower)
  44.                 if [ "$VERBOSE" == "on" ]; then
  45.                         echo "doing..lower on $LOOP .. newfile called $LOOP$EXT"
  46.                 fi      
  47.                 cat $LOOP | tr "[a-z]" "[A-Z]" > $LOOP$EXT
  48.                 ;;
  49.                 upper)
  50.                 if [ "$VERBOSE" == "on" ]; then
  51.                         echo "doing..lower on $LOOP ..newfile called $LOOP$EXT"
  52.                 fi      
  53.                 cat $LOOP | tr "[A-Z]" "[a-z]" >$LOOP$EXT
  54.                 ;;
  55.         esac   
  56. done   
复制代码
在脚本中指定命令行选项时,最好使其命名规则与U N I X或L I N U X一致。下面是一些选项
及其含义的列表。
选项含义
- a 扩展
- c 计数、拷贝
- d 目录、设备
- e 执行
- f 文件名、强制
- h 帮助
- i 忽略状态
- l 注册文件
- o 完整输出
- q 退出
- p 路径
-v 显示方式或版本

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
9 [报告]
发表于 2010-11-26 15:24 |只看该作者
20.3 小结

正确控制命令行选项会使脚本更加专业化,对于用户来说会使之看起来像一个系统命令。
本章讲到了控制命令行选项的两种方法, s h i f t和g e t o p t s。使用g e t o p t s检测脚本的数量远远小
于使用s h i f t方法检测脚本的数量。
s h i f t也克服了脚本参数$ 1 . . $ 9的限制。使用s h i f t命令,脚本可以很容易偏移至所有调用参
数,因此脚本可以做进一步处理。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
10 [报告]
发表于 2010-11-26 15:26 |只看该作者
shell脚本学习第二十篇,结束~~

END~~
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP