使用getopt函数实现命令行选项解析
在编写程序时,我们经常需要处理命令行参数。getopt函数是一个强大的工具,用于解析命令行选项。在本教程中,我们将学习如何使用getopt函数为我们的程序添加灵活的命令行接口。
基础知识
首先,让我们定义我们的程序需求:一个名为mydate
的程序,可以输出年月日,同时支持选项-y
、-m
、-d
、-H
、-M
和-S
。
命令行选项
每个命令行选项由一个字符组成,后面可以跟着一个或多个参数。例如,-y 2
表示两位数的年份。getopt函数的optstring
参数定义了可用的选项。
修改代码
我们从头开始编写代码,首先包含必要的头文件,并定义TIMESTRSIZE
变量。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <locale.h>
#define TIMESTRSIZE 1024
int main()
{
time_t stamp;
struct tm *tm;
char timestr[TIMESTRSIZE];
// 获取当前时间
time(&stamp);
tm = localtime(&stamp);
// 初始化时间字符串
strftime(timestr, TIMESTRSIZE, "Now:", tm);
// ... 省略了getopt函数的使用和解析逻辑 ...
printf("%s\n", timestr);
return 0;
}
接下来,我们添加getopt函数的调用,并处理不同的选项。
// 在命令行解析之前,设置默认区域
setlocale(LC_ALL, "");
int main(int argc, char **argv)
{
time_t stamp;
struct tm *tm;
char timestr[TIMESTRSIZE];
int c;
// 获取当前时间
time(&stamp);
tm = localtime(&stamp);
// 初始化时间字符串
strftime(timestr, TIMESTRSIZE, "Now:", tm);
// 解析命令行选项
while ((c = getopt(argc, argv, "HMSyymdS:")) != -1) {
switch (c) {
case 'H': // 选项H,输出小时
// 这里添加处理逻辑
break;
case 'M': // 选项M,输出分钟
// 这里添加处理逻辑
break;
case 'S': // 选项S,输出秒
// 这里添加处理逻辑
break;
case 'y': // 选项y,输出年份
// 检查选项是否带有参数
if (optarg) {
// 解析年份参数
// 这里添加处理逻辑
}
break;
case 'm': // 选项m,输出月份
// 这里添加处理逻辑
break;
case 'd': // 选项d,输出日
// 这里添加处理逻辑
break;
default:
printf("Invalid option: %c\n", c);
break;
}
}
// 打印最终的时间字符串
printf("%s\n", timestr);
return 0;
}
在以上代码中,我们仅仅是为了展示如何使用getopt函数进行了选项的循环处理,但在实际的选项处理逻辑中,你需要根据选项的要求进行相应的字符串格式化或输出。
处理带参数的选项
对于带参数的选项,如-y
,你需要检查optarg
是否为空,并据此进行处理。
case 'y':
if (optarg) {
// 假设我们想要输出4位或2位年份
if (strcmp(optarg, "4") == 0) {
strftime(timestr + strlen(timestr), TIMESTRSIZE - strlen(timestr), "%Y", tm);
} else if (strcmp(optarg, "2") == 0) {
strftime(timestr + strlen(timestr), TIMESTRSIZE - strlen(timestr), "%y", tm);
} else {
fprintf(stderr, "Invalid argument for -y: %s\n", optarg);
}
}
break;
处理非选项参数
getopt函数在遇到非选项参数时,会返回-1
,这时我们可以退出选项解析循环。
// ... 其他代码 ...
// 处理非选项参数
if (optind < argc) {
// 这里可以处理非选项参数
// 例如,打印非选项参数列表
for (int i = optind; i < argc; i++) {
printf("Non-option argument: %s\n", argv[i]);
}
}
// ... 其他代码 ...
完整的示例代码
下面是一个完整的示例代码,包括了处理选项和参数的逻辑。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <locale.h>
#define TIMESTRSIZE 1024
int main(int argc, char **argv)
{
setlocale(LC_ALL, "");
time_t stamp;
struct tm *tm;
char timestr[TIMESTRSIZE];
int c;
// 获取当前时间
time(&stamp);
tm = localtime(&stamp);
// 初始化时间字符串
strftime(timestr, TIMESTRSIZE, "Now:", tm);
// 解析命令行选项
while ((c = getopt(argc, argv, "HMSyymdS:")) != -1) {
switch (c) {
case 'H': // 选项H,输出小时
// 这里添加处理逻辑
break;
case 'M': // 选项M,输出分钟
// 这里添加处理逻辑
break;
case 'S': // 选项S,输出秒
// 这里添加处理逻辑
break;
case 'y': // 选项y,输出年份
if (optarg) {
if (strcmp(optarg, "4") == 0) {
strftime(timestr + strlen(timestr), TIMESTRSIZE - strlen(timestr), "%Y", tm);
} else if (strcmp(optarg, "2") == 0) {
strftime(timestr + strlen(timestr), TIMESTRSIZE - strlen(timestr), "%y", tm);
} else {
fprintf(stderr, "Invalid argument for -y: %s\n", optarg);
}
}
break;
case 'm': // 选项m,输出月份
// 这里添加处理逻辑
break;
case 'd': // 选项d,输出日
// 这里添加处理逻辑
break;
default:
printf("Invalid option: %c\n", c);
break;
}
}
// 打印最终的时间字符串
printf("%s\n", timestr);
// 处理非选项参数
if (optind < argc) {
for (int i = optind; i < argc; i++) {
printf("Non-option argument: %s\n", argv[i]);
}
}
return 0;
}
完整的mydate
程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define FMTSTRSIZE 255
#define TIMESTRSIZE 1024
int main(int argc, char **argv) {
FILE *fp = stdout;
time_t stamp;
struct tm *tm;
char timestr[TIMESTRSIZE];
int c;
char fmtstr[FMTSTRSIZE];
fmtstr[0] = '\0';
// 获取当前时间
stamp = time(NULL);
tm = localtime(&stamp);
// 解析命令行选项
while (1) {
c = getopt(argc, argv, "-H:MSy:md"); // getopt会返回选项字符,如果是-后面跟数字则是1
if (c < 0)
break;
switch (c) {
case 1:
fp = fopen(argv[optind - 1], "w"); // optind储存读到第几个参数的下标,读到文件地址时返回1,然后optind指向了下一个,所有要减一就是文件地址的参数
if (fp == NULL) {
perror("fopen()");
fp = stdout;
}
break;
case 'H': // 小时,24小时制
if (strcmp(optarg, "12") == 0)
strncat(fmtstr, " %I(%P)", FMTSTRSIZE);
else if (strcmp(optarg, "24") == 0)
strncat(fmtstr, " %H", FMTSTRSIZE);
else
fprintf(stderr, "Invalid argument of -H\n");
break;
case 'M': // 分钟
strncat(fmtstr, " %M", FMTSTRSIZE);
break;
case 'S': // 秒
strncat(fmtstr, " %S", FMTSTRSIZE);
break;
case 'y': // 年份
if (strcmp(optarg, "2") == 0)
strncat(fmtstr, " %y", FMTSTRSIZE);
else if (strcmp(optarg, "4") == 0)
strncat(fmtstr, " %Y", FMTSTRSIZE);
else
fprintf(stderr, "Invalid argument of -y\n");
break;
case 'm': // 月份
strncat(fmtstr, " %m", FMTSTRSIZE);
break;
case 'd': // 日
strncat(fmtstr, " %d", FMTSTRSIZE);
break;
default:
fprintf(stderr, "Invalid option: %c\n", optopt);
return 1;
}
}
// 初始化时间字符串
strncat(fmtstr, "\n", FMTSTRSIZE); // fputs不会自动换行,加个\n
strftime(timestr, TIMESTRSIZE, fmtstr, tm);
fputs(timestr, fp);
if (fp != stdout)
fclose(fp);
exit(0);
}