命令行多参数解析时getopt()函数的使用方法小结
在Linux中,用命令行执行可执行文件时,常常涉及到:大量、不同类型、不同形式的 输入参数问题。从简单的说起,现在假设有我们一个用户定义的可执行程序,名为test.sh,它需要3个输入参数,于是我们通过命令行去执行它的时候,往往通过如下的做法:
1 | ./test.sh 1 2 3 |
一. main函数
上面的./test.sh是执行程序,1、2、3是test.sh的输入参数,这些命令项通过传递给程序的main函数进行处理,main函数的一般形式如下:
1 | int main(int argc, char *argv[]); |
argc是一个整型,argv是一个指针数组,argc记录argv的大小,例如./test.sh 1 2 3将被以如下的方式传递:
1 | argc=4; |
二. getopt函数
现在我们考虑更复杂一些的输入要求,还是以test.sh为例,不过这时它的输入参数要更多一些了
1 | ./test.sh -1 -2 -3 -a 4 -b 5 -c 6 -Q -S -T // 此处仅作为示例,还可以有更多更复杂参数,见后续实例 |
先说说这一行参数表示什么意思,这里的破折号-表示这是一个控制选项,例如-1,在此处1是单字符选项,而-a 4表示带参数的选项,a是该选项的标识符,4是随同该选项一同传入的参数,-Q,与-1一样,Q也是单字符选项,没有随同的输入参数。通过定义不同的选项,我们可以在test.sh中定义丰富的操作完成各种各样的计算任务,但是这个时候main函数可没有办法给你完成上面的解析工作,main函数只是将./test.sh -1 -2 -3 -a 4 -b 5 -c 6 -Q -S -T传递进来,保存在argc和argv里,至于这些参数如何分配并对应什么操作则是需要用户自行定义了,当然,自己写解析函数是可行的,但是有更好的选择。
在C语言中,unistd.h提供的getopt()这个函数,结合switch语句,可以帮助我们方便实现参数解析。
先看例子:
1 | …… |
getopt()函数原型为:
1 | int getopt(int argc, char * const argv[], const char *optstring); |
使用getopt函数需要包含以下头文件:
1 |
有几个全局变量与getopt函数解析参数有关:
optind:int型, 指示下一个要解析的参数位置,初始时为1。
optarg:char *, 必须接参数的选项元素的参数, optarg 就指向参数字符串。
opterr: int 型, 设为0将不打印错误信。
int argc, char * const argv[]一般是直接通过读取main函数的argc和argv,而optstring则是用户定义的选项字符,例如在上面的例子中,optstring是 123a:b:c:QST ,它用来解析输入参数./test.sh -1 -2 -3 -a 4 -b 5 -c 6 -Q -S -T,并且是由用户定义。
字符串optstring的元素一般可分为下面几种:
- 单个字符,表示选项,
123a:b:c:QST中的1、2、3、Q、S、T都是单字符选项 - 单个字符后接一个冒号
:表示该选项后必须跟一个参数。参数紧跟在选项后以空格隔开。该参数的指针赋给optarg。123a:b:c:QST中的a:,b:,c:都表示它们需要附加指定输入参数,这就是为什么在输入参数时是-a 4 -b 5 -c 6的缘故 - 单个字符后跟两个冒号
::表示该选项后可选地跟一个参数。参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。(这个特性是GNU的扩张,本例中不作介绍)。
update@2016年5月17日17:11:51
一个选项的识别符号只能是单个字符(或加上与一个冒号:或两个冒号::的组合),不能用多个字符来表示一个选项。
例如这样做就会识别出错:
用户输入的参数./test.sh -1 -2 -3 -a1 4 -b 5 -c 6 -Q -S -T,那么它对应的optstring则是 123a1:b:c:QST,显然其中a1为两个字符,想让它表示一个选项,是会出现问题的。
同时,getopt()在unistd.h中的相关定义:
1 | * extern char *optarg; //选项的参数指针 |
那么现在就清楚用户输入的参数./test.sh -1 -2 -3 -a 4 -b 5 -c 6 -Q -S -T是怎么进行传递和解析的了:
- 通过main函数将输入参数
./test.sh -1 -2 -3 -a 4 -b 5 -c 6 -Q -S -T保存在argc和argv - getopt()按照
123a:b:c:QST这个规则去解析argc和argv中保存的数据 - 例如,首先去
./test.sh -1 -2 -3 -a 4 -b 5 -c 6 -Q -S -T读取到的选项是-1(第一个输入参数./test.sh是执行程序是名称不予考虑),于是就去123a:b:c:QST中检查是否有1这个选项,有的话就返回该选项(这个时候就会转入相应的case执行对应的操作),同时将选项索引optind更新为输入参数的下一个位置(此处为-2的位置)作为下次搜索的开始位置,如果在optstring里没有找到1,例如我们的optstring是23a:b:c:QST,即当命令行选项字符不包括在optstring中或者选项缺少必要的参数时,该选项存储在optopt中,getopt返回'?',并从optind开始进行下一个输入参数的解析 继续解析,当解析到
-a时,这个时候getopt()发现optstring里的a后面跟着:于是它知道a是还需要传递进来一个指定的参数,于是就将指针*optarg指向-a后面的一个参数即是4,这样返回选项a的时候,a所对应的参数值此时由optarg指向,这样转入case 'a'的时候就可以对该参数进行相应的操作了1
2
3case 'a':
para4 = atof(optarg);
break;接着往下走,-Q -S -T与前面的-1是一样的,都是不带参数的单字符选择,当检查完-T后,返回-1,表示检查完毕,这个时候就完成了对
./test.sh -1 -2 -3 -a 4 -b 5 -c 6 -Q -S -T中所有参数的分配工作。
上述简单介绍了命令行多参数解析时getopt()函数的用法,若有错误,欢迎斧正与探讨;若干概念引自文献[1],若有需要相关概念更详细的解释,可前往阅读。
三. 实例
在Eureka中进行的FSI数值算例中用到如下控制选项,整理出来作为备忘。
1 | 输入 |
| 控制符 | 值 | 含义 | |
|---|---|---|---|
| ./$1 | 执行程序名称,e.g. verification_oscillation_structure.gcc_v714M.st | ||
| -2 | 2 | 二维 | |
| -3 | 3 | 三维 | |
| -L | 290 | Length | |
| -H | 120 | Height | |
| -W | 0 | Width | |
| -J | 0.1 | critical_shear/maximum shear strain | |
| -h | 8 | element size that determines the patch thickness 入口单元尺寸 | |
| -D | 500 | Domain_End 计算域终点,计算域起点为0.0 | |
| -r | 1.5 | searchRange, parameters for the shape function | |
| -c | 1.0e-5 | cutoff, parameters for the shape function | |
| -x | 3.1 | extension, parameters for the shape function | |
| -b | 1.6 | beta, parameters for the shape function | |
| -q | 1 | integration_order q=1表示单元的中心插入1个质量点 | |
| -R | 1 | nRing 邻域控制,1表示从单元本身节点开始,如果变形大,可以设置为2 | |
| -M | 0.01 | mass_factor | |
| -S | true | adaptive_search | |
| -Q | true | adaptive_beta | |
| -U | true | updateNeighbor | |
| -t | 0.1 | time step ratio 0.1表示10% | |
| -T | 0.1 | total_time simulation time | |
| -d | 1000 | dump,number of time steps to visualize the results | |
| -v | 513 | max velocity of inflow,speed | |
| -f | 1.0 | parameters for the rupture of solids,epsilon_h=1 包含裂纹扩展 | |
| -G | 1.0e7 | parameters for the rupture of solids | |
| -k | 1.42e5 | the bulk modulus of fluid (2.1e9 Pa for water) 体积模量 | |
| -p | 1.18e-6 | the density of fluid (1.0e3 kg/m^3 for water) | |
| -g | 1.4 | Gruneisen parameter in the equation of state of the fluid | |
| -m | 0 | nuf,poisson’s ratio of the fluid | |
| -s | 1.82e-5 | the shear viscosity coefficient of fluid (1.82e-5kg/(m.s))动力粘度 | |
| -i | 0.1 | artificial viscosity coefficient to stablize the simulation 经验系数 | |
| -l | 10 | artificial viscosity coefficient to stablize the simulation 经验系数 | |
| -I | 0.0 | artificial viscosity coefficient to stablize the simulation 经验系数 | |
| -A | 0.0 | artificial viscosity coefficient to stablize the simulation 经验系数 | |
| -E | 2.5e6 | E_Solid,Young’s modulus of solid 固体材料弹性模量 | |
| -P | 1.0e-4 | rho_Solid,density of solid 固体材料密度 | |
| -N | 0.35 | nu_Solid,Poisson’s ratio of solid 固体材料泊松比 | |
| -n | 8 | number of threads 线程数 | |
| -K | 10 |
四. 参考文献
[1] http://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html#Using-Getopt
2016年2月1日16:18:12
于克利夫兰