getopt为解析命令行参数函数,它是Linux C库函数。使用此函数需要包含系统头文件unistd.h。
getopt函数声明如下:
int getopt(int argc, char * const argv[], const char * optstring);
其中函数的argc和argv参数通常直接从main的参数直接传递而来。optstring是一个包含合法选项字符的字符串。如果字符后跟一个冒号”:”,则该选项要求有参数。在argv中以“-“开头的都视作选项,“-“后的字符是选项字符。getopt会返回在argv中的选项字符,此字符会对应optstring中的字符。如果getopt被重复调用,它返回下一个选项字符。如果不再有可识别的选项字符,将返回-1。
如果getopt找到另一个选项字符,则返回该字符,更新外部变量optind和静态变量nextchar,以便下次调用getopt可以重复使用接下来的选项字符。如果成功找到选项,getopt返回该选项字符。如果所有命令行选项已被解析,getopt返回-1.如果getopt遇到一个选项字符不在optstring中,那么将返回"?".
如果getopt遇到一个缺少参数的选项,则返回值取决于optstring中的第一个字符,如果是":",则返回":",否则返回"?".
默认情况下,getopt会调换argv中的内容,将非选项放在最后。这样当getopts读取完所有的选项以后,optind会指向非选项的参数。
在处理选项列表时,getopt可以检测两种错误:(1).一个选项字符在optstring中并没有指定;(2).缺少选项参数。默认情况下,getopt在标准错误上输出错误信息,将错误的选项字符放在optopt中,并返回"?"作为函数结果。如果调用者将全局变量opterr设置为0,那么getopt不会输出错误信息(默认情况下,opterr是一个非零值)。如果optstring中的第一个字符是冒号":",那时getopt同样不会打印错误信息。另外,getopt将返回":"代替返回"?"以表示缺少选项参数。
getopt()所设置的全局变量包括:
(1). char *optarg:当前选项的参数。
(2). int optind: 是在argv中要处理的下一个元素的索引。系统初始化此值为1.调用者可以将其重置为1以重新开始扫描相同的argv,或扫描一个新的参数向量。每次调用getopt时,optind保存下个参数在argv中的索引(index)。如果找到一个选项,getopt会返回找到的选项字符,更新optind。如果选项有参数,将参数存到optarg,否则optarg为0。
(3). int opterr: 这个变量为非零时,getopt为”无效选项”或”缺少参数选项”输出错误信息。
(4). int optopt: 当发现无效选项字符时,getopt或返回'?'字符,或返回':'字符,并且optopt包含了所发现的无效选项字符。
getopt定义分为三种:
(1). 不带参数的选项。
(2). 必须带参数的选项:在选项后加一个冒号。
(3). 可选参数的选项:在选项后加两个冒号。
注意事项:
(1). 不带参数的选项可用连写。
(2). 选项不分先后顺序。
(3). 可选参数的选项与参数之间不能有空格。
下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:
CMakeLists.txt文件内容如下:
PROJECT(samples_cplusplus)
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
# 支持C++11
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -O2 -std=c11")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -O2 -std=c++11")
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})
FILE(GLOB samples ${PROJECT_SOURCE_DIR}/*.cpp)
FOREACH (sample ${samples})
STRING(REGEX MATCH "[^/]+$" sample_file ${sample})
STRING(REPLACE ".cpp" "" sample_basename ${sample_file})
ADD_EXECUTABLE(test_${sample_basename} ${sample})
TARGET_LINK_LIBRARIES(test_${sample_basename} pthread)
ENDFOREACH()
sample_getopt.cpp内容如下:
#include
#include
namespace {
void test1(int argc, char* argv[])
{
// reference: http://man7.org/linux/man-pages/man3/getopt.3.html
int flags = 0, opt = -1, nsecs = 0, tfnd = 0;
while ((opt = getopt(argc, argv, "nt:")) != -1) {
switch (opt) {
case 'n':
flags =1;
break;
case 't':
nsecs = atoi(optarg);
tfnd = 1;
break;
default:
fprintf(stderr, "Usage: %s [-t nsecs] [-n] name\n", argv[0]);
exit(EXIT_FAILURE);
}
}
fprintf(stdout, "flags = %d; tfnd = %d; nsec = %d; optind = %d\n", flags, tfnd, nsecs, optind);
if (optind >= argc) {
fprintf(stderr, "Expected argument after options\n");
exit(EXIT_FAILURE);
}
fprintf(stdout, "name argument = %s\n", argv[optind]);
exit(EXIT_SUCCESS);
}
int test2()
{
// reference: https://stackoverflow.com/questions/10502516/how-to-call-correctly-getopt-function
const char* argv[] = {"ProgramNameHere", "-f", "input.gmn", "-output.jpg"};
int argc = sizeof(argv) / sizeof(argv[0]);
std::cout<<"argc: "<
build.sh内容如下:
#! /bin/bash
real_path=$(realpath $0)
dir_name=`dirname "${real_path}"`
echo "real_path: ${real_path}, dir_name: ${dir_name}"
new_dir_name=${dir_name}/build
mkdir -p ${new_dir_name}
cd ${new_dir_name}
cmake ..
make
cd -
run_getopt.sh内容如下:
#! /bin/bash
real_path=$(realpath $0)
dir_name=`dirname "${real_path}"`
echo "real_path: ${real_path}, dir_name: ${dir_name}"
echo "test1:"
${dir_name}/build/test_sample_getopt 9 # params error
${dir_name}/build/test_sample_getopt 1 # flags = 0; tfnd = 0; nsec = 0; optind = 1
${dir_name}/build/test_sample_getopt 1 -b # invalid option -- 'b'
${dir_name}/build/test_sample_getopt 1 -x YYY # invalid option -- 'x'
${dir_name}/build/test_sample_getopt 1 -vZZZ # invalid option -- 'v'
${dir_name}/build/test_sample_getopt 1 -t 999 -n Jim # flags = 1; tfnd = 1; nsec = 999; optind = 4
${dir_name}/build/test_sample_getopt 1 -t888 -nSom # invalid option -- 'S'
${dir_name}/build/test_sample_getopt 1 -t6666 # flags = 0; tfnd = 1; nsec = 6666; optind = 2
${dir_name}/build/test_sample_getopt 1 -nant -t555 # invalid option -- 'a'
${dir_name}/build/test_sample_getopt 1 -n222 -t111 # invalid option -- '2'
echo -e "\n\ntest2:"
${dir_name}/build/test_sample_getopt 2
# argc: 4
# Option: f, argument: input.gmn
# Option: o, argument: utput.jpg
echo -e "\n\ntest3:"
${dir_name}/build/test_sample_getopt 3
# aflag = 0, bflag = 0, cvalue = (null)
# index: 1, Non-option argument: 3
${dir_name}/build/test_sample_getopt 3 -a -b
# aflag = 1, bflag = 1, cvalue = (null)
# index: 3, Non-option argument: 3
${dir_name}/build/test_sample_getopt 3 -ab
# aflag = 1, bflag = 1, cvalue = (null)
# iindex: 2, Non-option argument: 3
${dir_name}/build/test_sample_getopt 3 -c foo
# aflag = 0, bflag = 0, cvalue = foo
# index: 3, Non-option argument: 3
${dir_name}/build/test_sample_getopt 3 -cfoo
# aflag = 0, bflag = 0, cvalue = foo
# index: 2, Non-option argument: 3
${dir_name}/build/test_sample_getopt 3 arg1
# aflag = 0, bflag = 0, cvalue = (null)
# index: 1, Non-option argument: 3
# index: 2, Non-option argumnet: arg1
${dir_name}/build/test_sample_getopt 3 -a arg1
# aflag = 1, bflag = 0, cvalue = (null)
# index: 2, Non-option argument: 3
# index: 3, Non-option argument: arg1
${dir_name}/build/test_sample_getopt 3 -c foo arg1
# aflag = 0, bflag = 0, cvalue = foo
# index: 3, Non-option argument: 3
# index: 4, Non-option argument: arg1
${dir_name}/build/test_sample_getopt 3 -a -- -b
# aflag = 1, bflag = 0, cvalue = (null)
# index: 3, Non-option argument: 3
# index: 4, Non-option argument: -b
${dir_name}/build/test_sample_getopt 3 -a -
# aflag = 1, bflag = 0, cvalue = (null)
# index: 2, Non-option argument: 3
# index: 3, Non-option argument: -
执行过程:首先执行build.sh,然后再执行run_getopt.sh即可。
GitHub: https://github.com/fengbingchun/Linux_Code_Test