前言
Linux中,程序在命令行中经常需要添加参数运行,自己编写进行参数解析比较麻烦,可以使用GNU C提供的函数getopt
、getopt_long
、getopt_long_only
来快速解析命令行参数
关于命令行参数
命令行参数可以分为两类:
- 短选项,
-
- 长选项,
--
两者后面都可选择性添加额外参数。比如--block-size=SIZE
,SIZE便是额外的参数。
getopt_long函数
getopt函数只能处理短选项,而getopt_long函数两者都可以,函数声明如下:
#include <unistd.h>
extern char *optarg;
extern int optind, opterr, optopt;
#include <getopt.h>
int getopt(int argc, char * const argv[],const char *optstring);
int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
int getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
参数介绍
以上三个函数都适用
- argc和argv和main函数的两个参数一致。
optstring
: 表示短选项字符串,形式如a:b:c:d:
,分别表示程序支持的命令行短选项有-a、-b、-c、-d,冒号含义如下: 一个字符,后接一个冒号——表示选项后面带一个参数,如-a 100
longopts
:表示长选项结构体。结构如下:
4.struct option { const char *name; //选项名称 int has_arg; //后面是否跟参数, --num 100 int *flag; //一般为NULL int val; //当flag为NULL时,指定getopt_long函数返回val };
longindex
:一般为NULL,非空时记录当前找到参数在longopts中的下标值。
返回值介绍
- 如果短选项找到,那么将返回短选项对应的字符。
- 如果长选项找到,如果flag为NULL,返回val。如果flag不为空,返回0
- 如果遇到一个选项没有在短字符、长字符里面。或者在长字符里面存在二义性的,返回“?”
- 如果解析完所有字符没有找到(一般是输入命令参数格式错误,eg: 连斜杠都没有加的选项),返回“-1”
- 如果选项需要参数,忘了添加参数。返回值取决于optstring,如果其第一个字符是“:”,则返回“:”,否则返回“?”。
注意:
- longopts的最后一个元素必须是全0填充,否则会报段错误
- 短选项中每个选项都是唯一的。而长选项如果简写,也需要保持唯一性。
全局变量optarg
optarg
常用:表示当前选项对应的参数值。- optind:表示的是下一个将被处理到的参数在argv中的下标值。
- opterr:如果opterr = 0,在getopt、getopt_long、getopt_long_only遇到错误将不会输出错误信息到标准输出流。opterr在非0时,向屏幕输出错误。
- optopt:表示没有被未标识的选项。
示例程序
为了学习进程间通信之消息队列,下面以创建消息队列、发送数据到消息队列、读取消息队列数据三个进程为例进行示例:
创建消息队列
编译:gcc -o make_msgqueue make_msgqueue.c
执行:
./creat_msg_queue
Message Queue key: 67201.
Message queue id: 0.
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
int main() {
int messagequeueid;
key_t key;
//ftok 会根据这个文件的 inode,生成一个近乎唯一的 key
if((key = ftok("./messagequeuekey", 1024)) < 0) {
perror("ftok error");
exit(1);
}
printf("Message Queue key: %d.\n", key);
//创建消息队列
if ((messagequeueid = msgget(key, IPC_CREAT|0777)) == -1) {
perror("msgget error");
exit(1);
}
printf("Message queue id: %d.\n", messagequeueid);
}
发送消息
编译:gcc -o send send_msg.c
运行:./send -i 0 -t 123 -m "hello world"
可以看到后面跟了很多参数,需要快速解析出来
/*
下面的这段程序,getopt_long、do-while 循环以及 switch,是用来解析命令行参数的。
optarg变量为全局变量,用来承载解析后的参数
命令行参数的格式定义在 long_options 里面。
每一项的第一个成员“id”“type”“message”是参数选项的全称,第二个成员都为 1,表示参数选项后面要跟参数,最后一个成员’i’‘t’'m’是参数选项的简称。
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <getopt.h>
#include <string.h>
//消息类型
struct msg_buffer {
long mtype;
char mtext[1024];
};
int main(int argc, char *argv[]) {
int next_option;
//短选项
const char* const short_options = "i:t:m:";
//长选项
const struct option long_options[] = {
{ "id", 1, NULL, 'i'},
{ "type", 1, NULL, 't'},
{ "message", 1, NULL, 'm'},
{ NULL, 0, NULL, 0 }//最后一项必须全0,否则段错误
};
int messagequeueid = -1;//消息队列id
struct msg_buffer buffer;
buffer.mtype = -1;
int len = -1;
char * message = NULL;
do {
next_option = getopt_long (argc, argv, short_options, long_options, NULL);
switch (next_option)
{
case 'i':
messagequeueid = atoi(optarg);
break;
case 't':
buffer.mtype = atol(optarg);
break;
case 'm':
message = optarg;
len = strlen(message) + 1;
if (len > 1024) {
perror("message too long.");
exit(1);
}
memcpy(buffer.mtext, message, len);
break;
default:
break;
}
}while(next_option != -1);
if(messagequeueid != -1 && buffer.mtype != -1 && len != -1 && message != NULL){
if(msgsnd(messagequeueid, &buffer, len, IPC_NOWAIT) == -1){//发送数据到消息队列
perror("fail to send message.");
exit(1);
}
} else {
perror("arguments error");
}
return 0;
}
接收消息
编译:gcc -o recv recv_msg.c
运行:
./recv -i 0 -t 123
received message type : 123, text: hello world.
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <getopt.h>
#include <string.h>
struct msg_buffer {
long mtype;
char mtext[1024];
};
int main(int argc, char *argv[]) {
int next_option;
const char* const short_options = "i:t:";
const struct option long_options[] = {
{ "id", 1, NULL, 'i'},
{ "type", 1, NULL, 't'},
{ NULL, 0, NULL, 0 }
};
int messagequeueid = -1;
struct msg_buffer buffer;
long type = -1;
do {
next_option = getopt_long (argc, argv, short_options, long_options, NULL);
switch (next_option)
{
case 'i':
messagequeueid = atoi(optarg);
break;
case 't':
type = atol(optarg);
break;
default:
break;
}
}while(next_option != -1);
if(messagequeueid != -1 && type != -1){
if(msgrcv(messagequeueid, &buffer, 1024, type, IPC_NOWAIT) == -1){
perror("fail to recv message.");
exit(1);
}
printf("received message type : %d, text: %s.", buffer.mtype, buffer.mtext);
} else {
perror("arguments error");
}
return 0;
}