一站式搞定Bash脚本的参数处理问题
以下是来自StackOverflow网站的答案中的代码,写的实在是太好了,引用在这里,以供查阅。
第一段代码,这个例子展示了如何解析处理以空格分隔的参数(例如:–-option argument 这样的传参方式),不使用getopt和getopts函数来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #!/bin/bash # Usage: /tmp/demo-space-separated.sh -e conf -s /etc -l /usr/lib /etc/hosts POSITIONAL=() while [[ $# -gt 0 ]] do key="$1" case $key in -e|--extension) EXTENSION="$2" shift # past argument shift # past value ;; -s|--searchpath) SEARCHPATH="$2" shift # past argument shift # past value ;; -l|--lib) LIBPATH="$2" shift # past argument shift # past value ;; --default) DEFAULT=YES shift # past argument ;; *) # unknown option POSITIONAL+=("$1") # save it in an array for later shift # past argument ;; esac done set -- "${POSITIONAL[@]}" # restore positional parameters echo "FILE EXTENSION = ${EXTENSION}" echo "SEARCH PATH = ${SEARCHPATH}" echo "LIBRARY PATH = ${LIBPATH}" echo "DEFAULT = ${DEFAULT}" echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l) if [[ -n $1 ]]; then echo "Last line of file specified as non-opt/last argument:" tail -1 "$1" fi |
一些说明:
- POSITIONAL=() --- 声明了一个空的数组。详见这里。
- $# --- 这是一个表示参数个数的特殊变量。详见这里。
- $1, $2 --- 表示第一个参数,第二个参数的特殊变量。当然,$0表示第零个参数,一般保存的是脚本的名称。详见这里。
- “$1” --- 被双引号用括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。这点与单引号的处理方式不同。被单引号用括住的内容,将被视为单一字串。在引号内的代表变量的$符号,没有作用,也就是说,它被视为一般符号处理,防止任何变量扩展。详见这里。
- shift --- Shift命令一次移动参数的个数由其所带的参数指定。如果不带参数,则每次运行shift, 销毁一个参数,后面的参数前移。详见这里。
- POSITIONAL+=("$1") --- 向数组的最末的位置插入一个元素。详情见这里。
- ${POSITIONAL[@]} --- 这个语法的意思是取回数组中的所有变量,${arr[@]}。详情见这里。
- tail –1 “$1” --- tail 命令可用于查看文件的内容, tail –1 somefile.txt 会把somefile.txt的最后一行打印在屏幕上。
- if [[ -n $1 ]]; --- [[ 可以理解为更高级更现代的[, 即test命令。这里的语法对于用惯了高级语言的程序员会看起来很不习惯,感觉像if的条件里的比较少了一个操作数一样。把双方括号换成test命令就好理解了, 大概是这个样子:if (test –x option) then …
- test 命令有很多参数.
- -n 的意思是 “the length of STRING is nonzero”.
- -f 的意思是 “FILE exists and is a regular file”.
第二段代码,这个例子展示了如何门解析处理以等号分隔的参数(例如:–-option=argument 这样的传参方式),不使用getopt和getopts函数实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #!/bin/bash # Usage: /tmp/demo-equals-separated.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts for i in "$@" do case $i in -e=*|--extension=*) EXTENSION="${i#*=}" shift # past argument=value ;; -s=*|--searchpath=*) SEARCHPATH="${i#*=}" shift # past argument=value ;; -l=*|--lib=*) LIBPATH="${i#*=}" shift # past argument=value ;; --default) DEFAULT=YES shift # past argument with no value ;; *) # unknown option ;; esac done echo "FILE EXTENSION = ${EXTENSION}" echo "SEARCH PATH = ${SEARCHPATH}" echo "LIBRARY PATH = ${LIBPATH}" echo "DEFAULT = ${DEFAULT}" echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l) if [[ -n $1 ]]; then echo "Last line of file specified as non-opt/last argument:" tail -1 $1 fi |
一些说明:
- ${i#*=} --- 这里的语义通过上下文能看出来是移除了“-e=conf”这样一个字符串里的值为“-e=”的子字符串。简单的语法是${string#substring},即从string的前头开始,删除最短的substring的一个匹配,详情看这里的“Substring Removal”的部分。这个语句中的*是一个通配符(wildcard),*=的意思是任意字符直到遇到等号的子字符串。关于删除子字符串,这里给出了一个删除前缀和后缀的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #!/bin/sh # Usage: /tmp/demo-getopts.sh -vf /etc/hosts foo bar # A POSIX variable # Reset in case getopts has been used previously in the shell. OPTIND=1 # Initialize our own variables: output_file="" verbose=0 while getopts "h?vf:" opt; do case "$opt" in h|\?) show_help exit 0 ;; v) verbose=1 ;; f) output_file=$OPTARG ;; esac done shift $((OPTIND-1)) echo "verbose=$verbose, output_file='$output_file', Leftovers: $@" |
一些说明:
- $((OPTIND-1)) --- 这条语句中有两个知识点:
- $(( )) 的功能是进行算术运算,括号中的内容为数学表达式,使用 $(( )) 可以求数学表达式的值。这里的意思就是说会对OPTIND-1这个表达式进行算术运算,用以求值。详情请看这里。
- OPTIND 函数getopts配合case来进行操作时有两个隐含变量:一个是OPTARG,用来取当前选项的值,另外一个是OPTIND,代表下一个要处理的元素位置。OPTIND是一个特殊的变量,它的初始值是1,每次getopts处理完一个命令参数后就递增它,得到getopts要处理的下一个参数。详情见这里和这里。
- getopts "h?vf:" --- 这里的意思是getops 接受-h –v –f 为合规选项,?问号的意思就是不合规的选项进来的时候,就会执行给问好设定好的代码,程序员可以利用这个机制给用户提供恰当的信息指导。冒号的意思是关掉系统默认的处理不合规的选项的方式(disable the default error handling of invalid options),建议关掉系统默认的处理不合规选项。
对于这个使用getopts的实现方法:
- 优势:
- 可移植性更好,其他的类似的shell比如dash,可以兼容。
- 能自动地处理多重单字母选项,比如-vf filename, 这也是Unix的典型的处理方法。
- 劣势:
- 如果不额外添加代码的话,只能处理短选项,比如-h可以,但--help 就不行。
参考资料
==============
https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash
分类:
Linux and Unix
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2015-12-15 如何在Windows版的ScaleIO的节点中添加磁盘
2015-12-15 如何让我domain里的机器都跟domain controller的时间保持一致?
2009-12-15 Active Directory基础之三
2009-12-15 常用英语食品词汇- 调味品类