转至:https://www.cnblogs.com/eternityz/p/13879836.html

前提

在编写shell程序时经常需要处理命令行参数

选项与参数:

如下命令行:

  1. ./test.sh -f config.conf -v --prefix=/home
  1. -f为选项,它需要一个参数,即config.conf,
  2. -v 也是一个选项,但它不需要参数。
  3. --prefix我们称之为一个长选项,即选项本身多于一个字符,它也需要一个参数,用等号连接,当然等号不是必须的,
  4. /home可以直接写在--prefix后面,即--prefix/home,
  5. 更多的限制后面具体会讲到。

一.手工处理方式(已验证)

在手工处理方式中,首先要知道几个变量,还是以上面的命令行为例:


  1. $0:./test.sh,即命令本身,相当于c/c++中的argv[0]
  2. $1:-f,第一个参数.
  3. $2config.conf
  4. $3, $4 ... :类推。
  5. $#: 参数的个数,不包括命令本身,上例中$#为4.
  6. $@:参数本身的列表,也不包括命令本身,如上例为 -f config.conf -v --prefix=/home
  7. $*:和$@相同,但"$*" "$@"(加引号)并不同,"$*"将所有的参数解释成一个字符串,而"$@"是一个参数数组。

例子

  1. #!/bin/bash
  2. for arg in "$*"
  3. do
  4. echo $arg
  5. done
  6. for arg in "$@"
  7. do
  8. echo $arg
  9. done

执行./test.sh -f config.conf -n 10 会打印:

  1. #./test.sh -f config.conf -n 10
  2.  
  3. # 这是"$*"的输出
  4. -f config.conf -n 10
  5.  
  6. #以下为$@的输出
  7. -f
  8. config.conf
  9. -n
  10. 10

所以,手工处理的方式即对这些变量的处理。因为手工处理高度依赖于你在命令行上所传参数的位置,所以一般都只用来处理较简单的参数。

例如:

  1. ./test.sh 10

而很少使用./test -n 10这种带选项的方式。

典型用法为:

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

为什么要使用 x$1 != x 这种方式来比较呢?(x就是任意的一个字符,也可以是别的)

想像一下这种方式比较:

  1. if [ -n $1 ] #$1不为空
  1. 但如果用户不传参数的时候,$1为空,这时就会变成 [ -n ] ,所以需要加一个辅助字符串来进行比较。

手工处理方式能满足大多数的简单需求,配合shift使用也能构造出强大的功能,但在要处理复杂选项的时候建议用下面的两种方法。

二.getopts/getopt

处理命令行参数是一个相似而又复杂的事情,为此,c提供了getopt/getopt_long等函数,

c++的boost提供了options库,在shell中,处理此事的是getopts和getopt.

getopts和getopt功能相似但又不完全相同,其中getopt是独立的可执行文件,而getopts是由bash内置的。

  1. ./test.sh -a -b -c 短选项,各选项不需参数
  2. ./test.sh -abc 短选项,和上一种方法的效果一样,只是将所有的选项写在一起。
  3. ./test.sh -a args -b -c :短选项,其中-a需要参数,而-b -c不需参数。
  4. ./test.sh --a-long=args --b-long :长选项

先来看getopts,它不支持长选项。

使用getopts非常简单:

  1. #test.sh
  2. #!/bin/bash
  3. while getopts "a:bc" arg #选项后面的冒号表示该选项需要参数
  4. do
  5. case $arg in
  6. a)
  7. echo "a's arg:$optarg" #参数存在$optarg中
  8.  
  9. b)
  10. echo "b"
  11.  
  12. c)
  13. echo "c"
  14.  
  15. ?) #当有不认识的选项的时候arg为?
  16. echo "unkonw argument"
  17. exit 1
  18.  
  19. esac
  20. done

现在就可以使用:

  1. ./test.sh -a arg -b -c

  1. ./test.sh -a arg -bc

来加载了。

应该说绝大多数脚本使用该函数就可以了,如果需要支持长选项以及可选参数,那么就需要使用getopt.

getopt自带的一个例子:

  1. #!/bin/bash
  2. # a small example program for using the new getopt(1) program.
  3. # this program will only work with bash(1)
  4. # an similar program using the tcsh(1) script language can be found
  5. # as parse.tcsh
  6. # example input and output (from the bash prompt):
  7. # ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
  8. # option a
  9. # option c, no argument
  10. # option c, argument `more'
  11. # option b, argument ` very long '
  12. # remaining arguments:
  13. # --> `par1'
  14. # --> `another arg'
  15. # --> `wow!*\?'
  16. # note that we use `"$@"' to let each command-line parameter expand to a
  17. # separate word. the quotes around `$@' are essential!
  18. # we need temp as the `eval set --' would nuke the return value of getopt.
  19. #-o表示短选项,两个冒号表示该选项有一个可选参数,可选参数必须紧贴选项
  20. #如-carg 而不能是-c arg
  21. #--long表示长选项
  22. #"$@"在上面解释过
  23. # -n:出错时的信息
  24. # -- :举一个例子比较好理解:
  25. #我们要创建一个名字为 "-f"的目录你会怎么办?
  26. # mkdir -f #不成功,因为-f会被mkdir当作选项来解析,这时就可以使用
  27. # mkdir -- -f 这样-f就不会被作为选项。
  28. temp=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
  29. -n 'example.bash' -- "$@"`
  30. if [ $? != 0 ] ; then echo "terminating..." >&2 ; exit 1 ; fi
  31. # note the quotes around `$temp': they are essential!
  32. #set 会重新排列参数的顺序,也就是改变$1,$2...$n的值,这些值在getopt中重新排列过了
  33. eval set -- "$temp"
  34. #经过getopt的处理,下面处理具体选项。
  35. while true ; do
  36. case "$1" in
  37. -a|--a-long) echo "option a" ; shift ;;
  38. -b|--b-long) echo "option b, argument \`$2'" ; shift 2 ;;
  39. -c|--c-long)
  40. # c has an optional argument. as we are in quoted mode,
  41. # an empty parameter will be generated if its optional
  42. # argument is not found.
  43. case "$2" in
  44. "") echo "option c, no argument"; shift 2 ;;
  45. *) echo "option c, argument \`$2'" ; shift 2 ;;
  46. esac ;;
  47. --) shift ; break ;;
  48. *) echo "internal error!" ; exit 1 ;;
  49. esac
  50. done
  51. echo "remaining arguments:"
  52. for arg do
  53. echo '--> '"\`$arg'" ;
  54. done

比如使用

  1. ./test -a -b arg arg1 -c

你可以看到,命令行中多了个arg1参数,在经过getopt和set之后,命令行会变为:

  1. -a -b arg -c -- arg1

1指向−a, 1指向−a, 2指向-b,3指向arg, 3指向arg, 4指向-c,$5指向--,而多出的arg1则被放到了最后。

三.总结

一般小脚本手工处理也就够了,getopts能处理绝大多数的情况,getopt较复杂,功能也更强大。

站在巨人肩膀上摘苹果

https://www.jb51.net/article/48686.htm

shell脚本读取命令行的参数的更多相关文章

  1. Shell脚本中判断输入参数个数的方法投稿:junjie 字体:[增加 减小] 类型:转载

    Shell脚本中判断输入参数个数的方法 投稿:junjie 字体:[增加 减小] 类型:转载   这篇文章主要介绍了Shell脚本中判断输入参数个数的方法,使用内置变量$#即可实现判断输入了多少个参数 ...

  2. Shell脚本编写3---Shell 传递参数

    我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n.n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推…… 执行脚本,查看输出结果: 另外 ...

  3. awk如何区分shell脚本传进来的参数和自身的参数?awk如何获取shell脚本传进来的参数;awk中如何执行shell命令

    问题:对于shell脚本,$0表示脚本本身,$1表示脚本的第一个参数,$2……依次类推:对于awk,$1表示分割后的第一个字段,$2……依次类推.那么对于shell脚本中的awk如何区分两者呢? 答案 ...

  4. 【转】shell脚本中如何传入参数

    (1)直接用$1,$2取传入的参数vim /root/test.sh#!/bin/bashif [ $1 == "start" ]  then     echo "do ...

  5. 【shell脚本】优化内核参数===

    一.Linux内核参数优化 Sysctl命令用来配置与显示在/proc/sys目录中的内核参数.如果想使参数长期保存,可以通过编辑/etc/sysctl.conf文件来实现.  命令格式: sysct ...

  6. shell脚本获取传递的参数

    1 脚本编写 #!/bin/bash 2 解释 $n 表示是第几个参数 $0 表示脚本命令本身 3 执行效果

  7. 编写SHELL脚本--判断用户的参数

    测试语句格式: [ 条件表达式 ] 常见的几种形式: [ -d /etc ]  判断/etc是不是一个目录类型, [ -e /etc/php.ini ] 判断/etc/php.ini 文件是否存在 [ ...

  8. [shell]shell脚本传入不固定参数的写法,如--help等

    最近在调试一个wifi模块,需要传一些不固定的参数,一下一个参数解析的函数可以搞定这个事情,里面内容好多部分是从一个example中剽窃而来(窃喜) #!/bin/bash # writen by a ...

  9. Linux程序设计 读笔2 Shell脚本

    第二章 Shell程序设计 四.管道和重定向 1 重定向输出 ls -l > lsoutput.txt ps >> lsoutput.txt >>表示附加到一个文件中 文 ...

随机推荐

  1. POXSIX之信号量

    #include<stdio.h> #include<semaphore.h> #include<fcntl.h> #include<stdlib.h> ...

  2. python 线程池使用

    传统多线程方案会使用"即时创建, 即时销毁"的策略.尽管与创建进程相比,创建线程的时间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器将处于 ...

  3. SQL查询字段,起别名,列参与数学运算

    13.简单查询 13.1.查询一个字段? select 字段名 from 表名: 其中要注意: select和from都是关键字 字段名和表名都是标识符. 强调: 对于SQL语句说,是通用的 所有的S ...

  4. 谷歌浏览器和火狐浏览器如何查看HTTP协议

    谷歌浏览器和火狐浏览器如何查看HTTP协议 谷歌浏览器查看HTTP协议 火狐浏览器查看HTTP协议

  5. java-异常概述及体系

    1 package p1.exception; 2 3 4 /* 5 * 异常:是在运行时期发生的不正常情况. 6 * 7 * 8 * 在java中用类的形式对不正常情况进行了描述和封装对象. 9 * ...

  6. 图文并茂理解iptables

    原文地址:http://www.zsythink.net/archives/1199 以下是转载内容: iptables详解:图文并茂理解iptables | 朱双印博客 这篇文章会尽量以通俗易懂的方 ...

  7. 业务驱动的全景监控体系在阿里的应用 | 阿里巴巴DevOps实践指南

    编者按:本文源自阿里云云效团队出品的<阿里巴巴DevOps实践指南>,扫描上方二维码或前往:https://developer.aliyun.com/topic/devops,下载完整版电 ...

  8. 学习JAVAWEB第十八天

    今天解决了登录过程中的数据库连接池的一些问题,如本地服务器不能被访问,主要是连接池的配置文件的问题 明天做一个htm页面,不同用户的不同html页面

  9. kibana 对es的简单操作。

    一.查询和查看. #1.查询所有的数据 GET _search { "query": { "match_all":{} } } #2. 查看ES集群的健康状态 ...

  10. Arduino+ESP32 之 驱动GC9A01圆形LCD(二),移植LVGL,跑示例程序,显示自制图片

    在前文Arduino+ESP32 之 驱动GC9A01圆形LCD(一), 我们已经移植好了arduino GFX库, 该库的示例程序内,还有LVGL的示例程序哦. arduino环境下移植lvgl是很 ...