更新于:2024-04-09T23:18:47+08:00
一、Linux下的命令行处理
C 语言中命令行参数
执行程序时,可以从命令行传入参数给 C 的 main 函数。这些参数被称为命令行参数 ,它们对程序很重要,可以从外部控制程序的执行。
使用 main()
的函数参数可以处理命令行参数:
argc 是指传入参数的个数,包括最开头的执行程序名
argv[] 是一个指针数组,指向传递给程序的每个参数。
举例来说:
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main (int argc, char *argv[]) { if (argc == 2 ) printf ("The argument supplied is %s\n" , argv[1 ]); else if (argc > 2 ) printf ("Too many arguments supplied.\n" ); else printf ("One argument expected.\n" ); }
现在编译上面的程序,并执行 > ./a.out testing
,它会产生下列结果:
1 2 $./a.out testing The argument supplied is testing
getopt
但上面的机制还是过于简陋了。通常,命令行参数还带有很多选项(option),提供用户选择执行程序不同功能的机会,如查询版本一般用 -V
或者 --version
等,查看帮助文档一般使用 --helo
或 -h
等。为了方便处理,Linux 提供了更强大的函数,帮助开发者更好更快地解析命令行传来的参数。
先介绍 getopt()
函数:
1 2 3 4 5 6 #include <unistd.h> int getopt (int argc, char *const argv[], const char *optstring) ;extern char *optarg;extern int optind, opterr, optopt;
getopt()
函数的前两个参数之前已经介绍过了,第三个参数 optstring
是一个字符列表,其中的每个字母代表一个选项。
举例说明,optstring
的具体用法:当我们将 optstring = "ab:c::"
时,它表示:
单个字符 a:
表示选项a 没有参数,即在命令行输入 -a
即算打开该功能,类似于其他执行程序中查看帮助的 -h
。
单个字符加冒号 b:
表示选项b 在命令行输入时必须有参数,如 -b 10
。
单个字符加两个冒号 c::
表示选项c 在命令行输入时 -c
后跟的参数是可有可无的
getopt()
函数的返回值很有意思:
如果处理的 option 成功,那么返回选项的字母,如果有值跟随,那么字符串会被放在 optarg
中。
如果处理的 option 需要一个值,但命令行中没有给定值,返回 :
如果处理了一个未知的 option ,返回 ?
,并将值存入到 optopt
如果没有更多的 option 等待处理,返回 -1
如果多余出一些跟随值,那么会将多余的存放在argv
数组中,optind
和argc
分别充当索引和总数。
再注意一下全局变量 optind
opterr
optopt
,它们在解析命令行参数时非常重要。
extern char *optarg; 存放了正在被处理的 option 后跟的参数
extern int optind; 表下一个将被处理到的参数在 argv 中的下标值
extern int opterr; 正常运行状态下为 0。非零时表示存在无效选项或者缺少选项参数,并输出其错误信息
extern int optopt; 包含的发现未知 option 的无效选项字符
下面,放上一个简单的程序示例,例如要实现一个程序,要求有选项功能如下:> ./a.out -i -f file.txt -lr -x 'hero'
。
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 #include <stdio.h> #include <unistd.h> int main (int argc, char *argv[]) { int opt; while ((opt = getopt (argc, argv, ":if:lrx:" )) != -1 ) { switch (opt) { case 'i' : case 'l' : case 'r' : printf ("option: %c\n" , opt); break ; case 'f' : case 'x' : printf ("option: %c argname: %s\n" , opt, optarg); break ; case ':' : printf ("option needs a value\n" ); break ; case '?' : printf ("unknown option: %c\n" , optopt); break ; } } for (; optind < argc; optind++) { printf ("extra arguments: %s\n" , argv[optind]); } return 0 ; }
编译后运行,在命令行中输入 > ./a.out -i -f file.txt -lr -x hero
,则会输出:
1 2 3 4 5 option : ioption : f argname: file .txtoption : loption : roption : x argname: hero
而如果输入 > ./a.out -q -x
,则会:
1 2 3 unknown option : q option : x argname: -w extra arguments : 2
getopt_long
getopt_long()
函数与 getopt_long_only()
函数,它们的工作方式与 getopt()
函数很像,除了这些函数还可以接收(getopt_long_only()
除外)长选项--
,形式可以为--arg=param
或者--arg param
。getopt_long()
函数用法如下:
1 2 3 4 5 6 7 8 9 int getopt_long (int argc, char *const *argv, const char * shortopts, const struct option *longopts, int longind) ;struct option { const char *name; int has_arg; int *flag; int val; }
前两个参数相信早已不陌生,重点介绍后三个参数,optstring
是格式控制符,longopts
是 struct option
结构体组成的数组,该结构体表示的是“长参数”(即形如-–name
的参数)名称和性质:
name 长选项名称
has_arg 参数可选项,no_argument
表示该选项后不带参,required_argument
表示该选项后面带参数
*flag 匹配到选项后,如果flag是 NULL
,则返回val;如果不是 NULL
,则返回0,并且将 val
的值赋给flag指向的内存
val 匹配到选项后的返回值
longindex
,表示是当前处理 longopts
数组的下标值。
下面也给出个简单的例子,方便大家理解:
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 #include <stdio.h> #include <stdlib.h> #include <getopt.h> static int verbose_flag;int main (int argc, char **argv) { int c; while (1 ) { static struct option long_options[] = { {"verbose" , no_argument, &verbose_flag, 1 }, {"brief" , no_argument, &verbose_flag, 0 }, {"add" , no_argument, 0 , 'a' }, {"beyond" , no_argument, 0 , 'b' }, {"delete" , required_argument, 0 , 'd' }, {"create" , required_argument, 0 , 'c' }, {"file" , required_argument, 0 , 'f' } }; int option_index = 0 ; c = getopt_long (argc, argv, "abc:d:f:" , long_options, &option_index); if (c == -1 ) break ; switch (c) { case 0 : if (long_options[option_index].flag != 0 ) break ; printf ("option %s" , long_options[option_index].name); if (optarg) printf (" with arg %s" , optarg); printf ("\n" ); break ; case 'a' : printf ("option -a\n" ); break ; case 'b' : printf ("option -b\n" ); break ; case 'c' : printf ("option -c with value `%s'\n" , optarg); break ; case 'd' : printf ("option -d with value `%s'\n" , optarg); break ; case 'f' : printf ("option -f with value `%s'\n" , optarg); break ; case '?' : break ; default : abort (); } } printf ("verbose flag is %d\n" , verbose_flag); if (optind < argc) { printf ("non-option ARGV-elements: " ); while (optind < argc) printf ("%s " , argv[optind++]); putchar ('\n' ); } return 0 ; }
编译后执行,可以看下图结果:
二、Linux下的时间处理
Linux内核提供的基本时间服务是计算自协调世界时(UTC)公元1970年1月1日 00:00:00 这一特定时间以来经过的秒数。这种秒数用time_t
数据结构表示。
time()
函数返回当前的秒数。
1 2 3 #include <time.h> time_t time (time_t *calptr) ;
时间值作为函数值返回。若参数非空,时间值也会放在calptr
指向的值中。
clock_gettime()
函数可以用于获取指定时钟的时间,返回的时间在timespec
数据结构中,它将时间分为秒和纳秒。clock_id
用于指示选项,常用的有CLOCK_REALTIME
、CLOCK_MONOTONIC
、CLOCK_PROCESS_CPUTIME_ID
。
1 2 3 4 5 #include <sys/time.h> int clock_gettime (clockid_t clock_id, struct timespec *tsp) ;int clock_getres (clockid_t clock_id, struct timespec *tsp) ;int clock_settime (clockid_t clock_id, struct timespec *tsp) ;
clock_getres
函数把tsp
指向的timespec
结构初始化为clock_id
参数对于的时钟精度 。我们还可以使用clock_settime
函数设置时间,但有些时钟不能修改。
以上这些函数得到的数字都是自UTC时间的秒数,这对人类非常不友好。需要用localtime
、gmtime
、strftime
等函数将秒数转为可读时间。localtime
和gmtime
将时间转换存入到结构体tm
中。而mktime
函数将tm时间转换为秒数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <time.h> struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }struct tm *gmtime (const time_t *calptr);struct tm *localtime (const time_t *calptr);time_t mktime (struct tm *tmptr) ;
当然,gmtime
和localtime
函数仍然不能满足人们的需要。函数strftime
是类似于printf
的时间值函数,可以通过多个参数定制产生的字符串。
1 2 3 4 5 6 7 #include <time.h> size_t strftime (char *buf, size_t maxsize, const char *format, const struct tm *tmptr) ;size_t strftime_l (char *buf, size_t maxsize, const char *format, const struct tm *tmptr, locale_t locale) ;char *strptime (const char *buf, const char *format, struct tm *tmptr) ;
tmptr
是要格式化的时间值,格式化的结果存放在长度为maxsize
的buf
数组中,如果长度不足,函数返回0,否则返回在 buf
中存放的字符数。format
是控制时间值的格式,与printf
相同。
这是使用说明:
strptime
是strftime
的反过来的版本,把字符串时间转换为分解时间。
这些函数的转换关系,可以用这一张图概括: