2

在 Linux 中更好地使用C/C++语言

 2 years ago
source link: https://dingfen.github.io/cpp/2021/08/25/tricks-in-LinuxC.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一、Linux下的命令行处理Permalink

C 语言中命令行参数Permalink

执行程序时,可以从命令行传入参数给 C 的 main 函数。这些参数被称为命令行参数,它们对程序很重要,可以从外部控制程序的执行。

使用 main() 的函数参数可以处理命令行参数:

  • argc 是指传入参数的个数,包括最开头的执行程序名
  • argv[] 是一个指针数组,指向传递给程序的每个参数。

举例来说:

#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,它会产生下列结果:

$./a.out testing
The argument supplied is testing

getoptPermalink

但上面的机制还是过于简陋了。通常,命令行参数还带有很多选项(option),提供用户选择执行程序不同功能的机会,如查询版本一般用 -V 或者 --version 等,查看帮助文档一般使用 --helo-h 等。为了方便处理,Linux 提供了更强大的函数,帮助开发者更好更快地解析命令行传来的参数。

先介绍 getopt() 函数:

#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数组中,optindargc分别充当索引和总数。

再注意一下全局变量 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'

#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
  int opt;      
  // put ':' in the starting of the 
  // string so that program can  
  // distinguish between '?' and ':'  
  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;
      }
  }
  // optind is for the extra arguments 
  // which are not parsed 
  for(; optind < argc; optind++) {      
    printf("extra arguments: %s\n", argv[optind]);  
  }     
  return 0; 
}

编译后运行,在命令行中输入 > ./a.out -i -f file.txt -lr -x hero,则会输出:

option: i
option: f argname: file.txt
option: l
option: r
option: x argname: hero

而如果输入 > ./a.out -q -x,则会:

unknown option: q
option: x argname: -w
extra arguments: 2

getopt_longPermalink

getopt_long() 函数与 getopt_long_only() 函数,它们的工作方式与 getopt() 函数很像,除了这些函数还可以接收(getopt_long_only()除外)长选项--,形式可以为--arg=param或者--arg paramgetopt_long() 函数用法如下:

int getopt_long(int argc, char *const *argv, const char* shortopts, 
                const struct option *longopts, int longind);
// 其中结构体option
struct option {
    const char *name; // name of ong option
    int has_arg;  // 0 no 1 required 2 optional
    int *flag;    // how results returned
    int val;      // value to return
}

前两个参数相信早已不陌生,重点介绍后三个参数,optstring 是格式控制符,longoptsstruct option 结构体组成的数组,该结构体表示的是“长参数”(即形如-–name 的参数)名称和性质:

  • name 长选项名称
  • has_arg 参数可选项,no_argument 表示该选项后不带参,required_argument 表示该选项后面带参数
  • *flag 匹配到选项后,如果flag是 NULL ,则返回val;如果不是 NULL ,则返回0,并且将 val 的值赋给flag指向的内存
  • val 匹配到选项后的返回值

longindex,表示是当前处理 longopts 数组的下标值。

下面也给出个简单的例子,方便大家理解:

#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_flag */
        {"verbose", no_argument, &verbose_flag, 1},
        {"brief", no_argument, &verbose_flag, 0},
        /* 下面的选项返回值是val值 no_argument required_argument是宏常量 */
        {"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'}
        };

    /* getopt_long 数组索引 */
    int option_index = 0;
    c = getopt_long (argc, argv, "abc:d:f:",
                long_options, &option_index);
    /* 返回-1 循环结束 */
    if (c == -1)
        break;
    switch (c) {
        case 0:
            /* If this option set a flag, do nothing else now. */
            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 '?':
            /* getopt_long already printed an error message. */
            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下的时间处理Permalink

Linux内核提供的基本时间服务是计算自协调世界时(UTC)公元1970年1月1日 00:00:00 这一特定时间以来经过的秒数。这种秒数用 time_t 数据结构表示。

time()函数返回当前的秒数。

#include <time.h>

time_t time(time_t *calptr);

时间值作为函数值返回。若参数非空,时间值也会放在calptr指向的值中。

clock_gettime()函数可以用于获取指定时钟的时间,返回的时间在timespec数据结构中,它将时间分为秒和纳秒。clock_id用于指示选项,常用的有CLOCK_REALTIMECLOCK_MONOTONICCLOCK_PROCESS_CPUTIME_ID

#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时间的秒数,这对人类非常不友好。需要用localtimegmtimestrftime等函数将秒数转为可读时间。localtimegmtime将时间转换存入到结构体tm中。而mktime函数将tm时间转换为秒数。

#include <time.h>
struct tm {
    int tm_sec;		// [0-60] 允许润秒
    int tm_min;		// [0-59]
    int tm_hour;	// [0-23] 
    int tm_mday;	// [1-31]
    int tm_mon;		// [0-11]
    int tm_year;	// years since 1900
    int tm_wday;	// [0-6]
    int tm_yday;	// [0-365]
    int tm_isdst;	// daylight saving time flag
}

struct tm *gmtime(const time_t *calptr);
struct tm *localtime(const time_t *calptr);

time_t mktime(struct tm *tmptr);

当然,gmtimelocaltime函数仍然不能满足人们的需要。函数strftime是类似于printf的时间值函数,可以通过多个参数定制产生的字符串。

#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是要格式化的时间值,格式化的结果存放在长度为maxsizebuf数组中,如果长度不足,函数返回0,否则返回在 buf 中存放的字符数。format是控制时间值的格式,与printf相同。

这是使用说明:

strptimestrftime的反过来的版本,把字符串时间转换为分解时间。

这些函数的转换关系,可以用这一张图概括:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK