glibc 知:手册25:程序基础/系统接口

1. 前言

The GNU C Library Reference Manual for version 2.35

2. 基本程序/系统接口

The Basic Program/System Interface

进程是分配系统资源的原始单位。每个进程都有自己的地址空间和(通常)一个控制线程。一个进程执行一个程序;您可以让多个进程执行同一个程序,但每个进程在其自己的地址空间内都有自己的程序副本,并独立于其他副本执行它。尽管它可能在同一个程序中有多个控制线程,并且一个程序可能由多个逻辑上独立的模块组成,但一个进程总是只执行一个程序。

请注意,出于本手册的目的,我们使用“程序”的特定定义,它对应于 Unix 系统上下文中的通用定义。在流行的用法中,“程序”的定义要广泛得多。例如,它可以指代系统的内核、编辑器宏、复杂的软件包或在进程中执行的离散代码部分。

编写程序是本手册的全部内容。本章解释了你的程序和运行或调用它的系统之间最基本的接口。这包括从系统传递参数(参数和环境),从系统请求基本服务,以及告诉系统程序完成。

一个程序使用 exec 系列系统调用启动另一个程序。本章从被执行人的角度来看程序启动。要从执行者的角度查看事件,请参阅执行文件

2.1. 程序参数

Program Arguments

系统通过调用函数 main 来启动一个 C 程序。编写一个名为 main 的函数由您自己决定——否则,您甚至无法无错误地链接您的程序。

在 ISO C 中,您可以将 main 定义为不带参数,或者带两个参数来表示程序的命令行参数,如下所示:

int main (int argc, char *argv[])

命令行参数是用于调用程序的 shell 命令中给出的以空格分隔的标记;因此,在“cat foo bar”中,参数是“foo”和“bar”。程序查看其命令行参数的唯一方法是通过 main 的参数。如果 main 不带参数,那么您将无法进入命令行。

argc 参数的值是命令行参数的数量。argv 参数是 C 字符串的向量;它的元素是单独的命令行参数字符串。正在运行的程序的文件名也作为第一个元素包含在向量中;argc 的值计算这个元素。空指针总是跟在最后一个元素后面:argv[argc] 就是这个空指针。

对于命令“cat foo bar”,argc 为 3,而 argv 包含三个元素,“cat”、“foo”和“bar”。

在 Unix 系统中,您可以定义 main 第三种方式,使用三个参数:

int main (int argc, char *argv[], char *envp[])

前两个参数是一样的。第三个参数 envp 给出了程序的环境;它与environ的值相同。请参阅环境变量。POSIX.1 不允许这种三参数形式,因此为了便于移植,最好编写 main 带两个参数,并使用 environ 的值。

2.1.1. 程序参数语法约定

Program Argument Syntax Conventions

POSIX 建议将这些约定用于命令行参数。getopt(请参阅使用 getopt 解析程序选项)和 argp_parse(请参阅使用 Argp 解析程序选项)使实现它们变得容易。

  • 如果参数以连字符分隔符 (‘-’) 开头,则参数是选项。

  • 如果选项不带参数,则多个选项可以在单个标记中跟随连字符分隔符。因此,“-abc”等价于“-a -b -c”。

  • 选项名称是单个字母数字字符(对于 isalnum;请参阅字符分类)。

  • 某些选项需要参数。例如,ld 命令的 -o 选项需要一个参数——一个输出文件名。

  • 选项及其参数可能会或可能不会显示为单独的标记。(换句话说,分隔它们的空格是可选的。)因此,-o foo 和 -ofoo 是等价的。

  • 选项通常在其他非选项参数之前。

    GNU C 库中 getopt 和 argp_parse 的实现通常使它看起来好像所有选项参数都在所有非选项参数之前指定以进行解析,即使您的程序的用户混合了选项和非选项参数.他们通过重新排序 argv 数组的元素来做到这一点。这种行为是非标准的;如果要抑制它,请定义 _POSIX_OPTION_ORDER 环境变量。请参阅标准环境变量

  • 参数 – 终止所有选项;任何后续参数都被视为非选项参数,即使它们以连字符开头。

  • 由单个连字符组成的标记被解释为普通的非选项参数。按照惯例,它用于指定来自标准输入和输出流的输入或输出。

  • 选项可以按任何顺序提供,也可以多次出现。解释由特定的应用程序决定。

GNU 为这些约定添加了长选项。长选项包括 – 后跟由字母数字字符和破折号组成的名称。选项名称通常为一到三个单词,并用连字符分隔单词。只要缩写是唯一的,用户就可以缩写选项名称。

要为长选项指定参数,请编写 --name=value。此语法允许长选项接受本身是可选的参数。

最终,GNU 系统将为 shell 中的长选项名称提供补全。

2.1.2. 解析程序参数

Parsing Program Arguments

如果程序的命令行参数的语法足够简单,您可以简单地手动从 argv 中挑选参数。但是除非您的程序采用固定数量的参数,或者所有参数都以相同的方式解释(例如文件名),否则您通常最好使用 getopt(请参阅使用 getopt 解析程序选项)或 argp_parse(请参阅使用 Argp 解析程序选项)进行解析。

getopt 更标准(它的短选项版本是 POSIX 标准的一部分),但是对于非常简单和非常复杂的选项结构,使用 argp_parse 通常更容易,因为它为您做了更多的脏工作。

2.2. 使用 getopt 解析程序选项

Parsing program options using getopt

getopt 和 getopt_long 函数自动完成解析典型 unix 命令行选项所涉及的一些琐事。

2.2.1. 使用 getopt 函数

Using the getopt function

以下是有关如何调用 getopt 函数的详细信息。要使用此功能,您的程序必须包含头文件 unistd.h。

变量:int opterr

如果此变量的值非零,则 getopt 会在遇到未知选项字符或缺少必需参数的选项时将错误消息打印到标准错误流。这是默认行为。如果将此变量设置为零,getopt 不会打印任何消息,但仍会返回字符 ?指示错误。

变量:int optopt

当 getopt 遇到未知选项字符或缺少必需参数的选项时,它将该选项字符存储在此变量中。您可以使用它来提供您自己的诊断消息。

变量:int optind

此变量由 getopt 设置为要处理的 argv 数组的下一个元素的索引。一旦 getopt 找到所有选项参数,您就可以使用此变量来确定其余非选项参数的开始位置。该变量的初始值为 1。

变量:char * optarg

对于那些接受参数的选项,此变量由 getopt 设置为指向选项参数的值。

函数:int getopt (int argc, char *const *argv, const char *options)

Preliminary: | MT-Unsafe race:getopt env | AS-Unsafe heap i18n lock corrupt | AC-Unsafe mem lock corrupt | See POSIX Safety Concepts.

getopt 函数从 argv 和 argc 参数指定的参数列表中获取下一个选项参数。通常这些值直接来自 main 接收到的参数。

options 参数是一个字符串,它指定对该程序有效的选项字符。此字符串中的选项字符后面可以跟一个冒号(‘:’),表示它需要一个必需的参数。如果选项字符后跟两个冒号(‘::’),则其参数是可选的;这是一个 GNU 扩展。

getopt 有三种方法来处理非选项 argv 元素之后的选项。特殊参数“–”在所有情况下都强制结束选项扫描。

  • 默认设置是在扫描时置换 argv 的内容,以便最终所有非选项都位于末尾。这允许以任何顺序给出选项,即使是编写的程序没有预料到这一点。
  • 如果选项参数字符串以连字符 (‘-’) 开头,则对其进行特殊处理。它允许返回不是选项的参数,就好像它们与选项字符’\1’相关联。
  • POSIX 要求以下行为:第一个非选项停止选项处理。通过设置环境变量 POSIXLY_CORRECT 或以加号 (‘+’) 开头的选项参数字符串来选择此模式。

getopt 函数返回下一个命令行选项的选项字符。当没有更多的选项参数可用时,它返回 -1。可能还有更多的非选项参数;您必须将外部变量 optind 与 argc 参数进行比较以进行检查。

如果选项有参数,getopt 会通过将参数存储在变量 optarg 中来返回参数。您通常不需要复制 optarg 字符串,因为它是指向原始 argv 数组的指针,而不是指向可能被覆盖的静态区域的指针。

如果 getopt 在 argv 中发现未包含在选项中的选项字符,或者缺少选项参数,它会返回“?”并将外部变量 optopt 设置为实际的选项字符。如果选项的第一个字符是冒号(‘:’),则 getopt 返回‘:’而不是‘?’以指示缺少选项参数。此外,如果外部变量 opterr 不为零(这是默认值),getopt 会打印一条错误消息。

2.2.2. 使用 getopt 解析参数的示例

Example of Parsing Arguments with getopt

下面是一个示例,展示了通常如何使用 getopt。需要注意的关键点是:

  • 通常,getopt 在循环中被调用。当 getopt 返回 -1 表示没有更多选项存在时,循环终止。
  • switch 语句用于调度 getopt 的返回值。在典型使用中,每种情况只设置一个稍后在程序中使用的变量。
  • 第二个循环用于处理剩余的非选项参数。
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main (int argc, char **argv)
{
  int aflag = 0;
  int bflag = 0;
  char *cvalue = NULL;
  int index;
  int c;

  opterr = 0;

  while ((c = getopt (argc, argv, "abc:")) != -1)
    switch (c)
      {
      case 'a':
        aflag = 1;
        break;
      case 'b':
        bflag = 1;
        break;
      case 'c':
        cvalue = optarg;
        break;
      case '?':
        if (optopt == 'c')
          fprintf (stderr, "Option -%c requires an argument.\n", optopt);
        else if (isprint (optopt))
          fprintf (stderr, "Unknown option `-%c'.\n", optopt);
        else
          fprintf (stderr,
                   "Unknown option character `\\x%x'.\n",
                   optopt);
        return 1;
      default:
        abort ();
      }

  printf ("aflag = %d, bflag = %d, cvalue = %s\n",
          aflag, bflag, cvalue);

  for (index = optind; index < argc; index++)
    printf ("Non-option argument %s\n", argv[index]);
  return 0;
}

以下是一些示例,展示了该程序使用不同的参数组合打印的内容:

% testopt
aflag = 0, bflag = 0, cvalue = (null)

% testopt -a -b
aflag = 1, bflag = 1, cvalue = (null)

% testopt -ab
aflag = 1, bflag = 1, cvalue = (null)

% testopt -c foo
aflag = 0, bflag = 0, cvalue = foo

% testopt -cfoo
aflag = 0, bflag = 0, cvalue = foo

% testopt arg1
aflag = 0, bflag = 0, cvalue = (null)
Non-option argument arg1

% testopt -a arg1
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument arg1

% testopt -c foo arg1
aflag = 0, bflag = 0, cvalue = foo
Non-option argument arg1

% testopt -a -- -b
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument -b

% testopt -a -
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument -

2.2.3. 使用 getopt_long 解析长选项

Parsing Long Options with getopt_long

要接受 GNU 样式的长选项以及单字符选项,请使用 getopt_long 而不是 getopt。此函数在 getopt.h 中声明,而不是在 unistd.h 中。如果每个程序使用任何选项,您应该让每个程序都接受长选项,因为这几乎不需要额外的工作,并且可以帮助初学者记住如何使用该程序。

数据类型:struct option

为了 getopt_long,此结构描述了一个长选项名称。参数 longopts 必须是这些结构的数组,每个长选项一个。使用包含全零的元素终止数组。

struct 选项结构具有以下字段:

const char *name

该字段是选项的名称。它是一个字符串。

int has_arg

该字段说明选项是否接受参数。它是一个整数,存在三个合法值:no_argument、required_argument 和 optional_argument。

int *flag

int val

这些字段控制在选项发生时如何报告或处理选项。

如果 flag 是空指针,则 val 是标识此选项的值。通常选择这些值来唯一标识特定的多头期权。

如果 flag 不是空指针,它应该是一个 int 变量的地址,它是这个选项的标志。val 中的值是存储在标志中的值,以指示看到该选项。

函数:int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *indexptr)

Preliminary: | MT-Unsafe race:getopt env | AS-Unsafe heap i18n lock corrupt | AC-Unsafe mem lock corrupt | See POSIX Safety Concepts.

从向量 argv(长度为 argc)解码选项。参数 shortopts 描述了要接受的短选项,就像它在 getopt 中所做的那样。参数 longopts 描述了接受的长选项(见上文)。

当 getopt_long 遇到一个短选项时,它会做与 getopt 相同的事情:它返回选项的字符代码,并将选项的参数(如果有的话)存储在 optarg 中。

当 getopt_long 遇到 long 选项时,它会根据该选项定义的 flag 和 val 字段执行操作。

如果 flag 是空指针,则 getopt_long 返回 val 的内容以指示它找到了哪个选项。您应该在 val 字段中为具有不同含义的选项安排不同的值,以便您可以在 getopt_long 返回后解码这些值。如果 long 选项等价于 short 选项,则可以在 val 中使用 short 选项的字符代码。

如果 flag 不是空指针,这意味着这个选项应该只在程序中设置一个标志。该标志是您定义的 int 类型的变量。将标志的地址放在标志字段中。在 val 字段中输入您希望此选项存储在标志中的值。在这种情况下,getopt_long 返回 0。

对于任何长选项,getopt_long 会告诉您选项定义的数组 longopts 中的索引,方法是将其存储到 *indexptr 中。您可以使用 longopts[*indexptr].name 获取选项的名称。因此,您可以通过 val 字段中的值或索引来区分长选项。您还可以通过这种方式区分设置标志的长选项。

当 long 选项有参数时,getopt_long 在返回之前将参数值放入变量 optarg 中。当该选项没有参数时,Optarg中的值是一个空指针。这是您如何判断是否提供了可选参数的方法。

当 getopt_long 没有更多选项要处理时,它返回 -1,并在变量 optind 中保留下一个剩余参数的 argv 索引。

由于在 getopt_long 发明之前使用了长选项名称,因此程序接口要求程序识别诸如“-option value”而不是“–option value”之类的选项。为了使这些程序能够使用 GNU getopt 功能,还有一个可用的功能。

函数:int getopt_long_only (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *indexptr)

Preliminary: | MT-Unsafe race:getopt env | AS-Unsafe heap i18n lock corrupt | AC-Unsafe mem lock corrupt | See POSIX Safety Concepts.

getopt_long_only 函数等价于 getopt_long 函数,但它允许应用程序的用户只使用“-”而不是“–”来传递长选项。‘–’ 前缀仍然可以识别,但如果看到“-”,则不会查看短选项,而是首先尝试此参数是否命名为长选项。如果不是,则将其解析为短选项。

假设使用 getopt_long_only 启动应用程序

  app -foo

getopt_long_only 将首先查找名为“foo”的长选项。如果未找到,则会识别短选项“f”、“o”和“o”。

2.2.4. 使用 getopt_long 解析长选项的示例

Example of Parsing Long Options with getopt_long

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

/* Flag set by ‘--verbose’. */
static int verbose_flag;

int
main (int argc, char **argv)
{
  int c;

  while (1)
    {
      static struct option long_options[] =
        {
          /* These options set a flag. */
          {"verbose", no_argument,       &verbose_flag, 1},
          {"brief",   no_argument,       &verbose_flag, 0},
          /* These options don’t set a flag.
             We distinguish them by their indices. */
          {"add",     no_argument,       0, 'a'},
          {"append",  no_argument,       0, 'b'},
          {"delete",  required_argument, 0, 'd'},
          {"create",  required_argument, 0, 'c'},
          {"file",    required_argument, 0, 'f'},
          {0, 0, 0, 0}
        };
      /* getopt_long stores the option index here. */
      int option_index = 0;

      c = getopt_long (argc, argv, "abc:d:f:",
                       long_options, &option_index);

      /* Detect the end of the options. */
      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':
          puts ("option -a\n");
          break;

        case 'b':
          puts ("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 ();
        }
    }

  /* Instead of reporting ‘--verbose’
     and ‘--brief’ as they are encountered,
     we report the final status resulting from them. */
  if (verbose_flag)
    puts ("verbose flag is set");

  /* Print any remaining command line arguments (not options). */
  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
        printf ("%s ", argv[optind++]);
      putchar ('\n');
    }

  exit (0);
}

2.3. 使用 Argp 解析程序选项

Parsing Program Options with Argp

Argp 是一个用于解析 unix 风格的参数向量的接口。请参阅程序参数

Argp 提供了更常用的 getopt 接口中不可用的功能。这些功能包括自动生成输出以响应“–help”和“–version”选项,如 GNU 编码标准中所述。使用 argp 可以减少程序员忽略实现这些附加选项或使它们保持最新的可能性。

Argp 还提供了将几个独立定义的选项解析器合并为一个的能力,调解它们之间的冲突并使结果看起来无缝。库可以导出用户程序可能与他们自己的选项解析器一起使用的 argp 选项解析器,从而减少用户程序的工作量。一些程序可能只使用库导出的参数解析器,从而对库实现的抽象实现一致且高效的选项解析。

应包含头文件 <argp.h> 以使用 argp。

2.3.1. argp_parse 函数

The argp_parse Function

argp 的主要接口是 argp_parse 函数。在许多情况下,调用 argp_parse 是 main 中唯一需要的参数解析代码。请参阅程序参数

函数:error_t argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags, int *arg_index, void *input)

Preliminary: | MT-Unsafe race:argpbuf locale env | AS-Unsafe heap i18n lock corrupt | AC-Unsafe mem lock corrupt | See POSIX Safety Concepts.

argp_parse 函数使用 argp 解析器 argp 解析 argv 中长度为 argc 的参数。请参阅指定 Argp 解析器。为 argp 传递空指针与使用包含全零的 struct argp 相同。

flags 是一组修改解析行为的标志位。请参阅 argp_parse 的标志。输入被传递到 argp 解析器 argp,并具有由 argp 定义的含义。一个典型的用法是传递一个指向结构的指针,该结构用于为解析器指定参数并传回结果。

除非 ARGP_NO_EXIT 或 ARGP_NO_HELP 标志包含在标志中,否则调用 argp_parse 可能会导致程序退出。如果检测到错误或遇到未知选项,则此行为为真。请参阅程序终止

如果 arg_index 不为空,则 argv 中第一个未解析选项的索引作为值返回。

成功解析的返回值为零,如果检测到错误,则返回错误代码(请参阅错误代码)。不同的 argp 解析器可能会返回任意错误代码,但标准错误代码是:如果发生内存分配错误,则为 ENOMEM,如果遇到未知选项或选项参数,则为 EINVAL。

2.3.2. Argp 全局变量

Argp Global Variables

这些变量使用户程序可以轻松实现“–version”选项并在“–help”输出中提供错误报告地址。这些默认情况下在 argp 中实现。

变量:const char * argp_program_version

如果用户程序定义或设置为非零值,则在使用 argp_parse 解析时添加“–version”选项,该选项将打印“–version”字符串,后跟换行符并退出。如果使用了 ARGP_NO_EXIT 标志,则例外。

变量:const char * argp_program_bug_address

如果由用户程序定义或设置为非零值,则 argp_program_bug_address 应指向一个字符串,该字符串将在“–help”选项的标准输出末尾打印,嵌入在“报告错误”的句子中讲话。'。

变量:argp_program_version_hook

如果用户程序定义或设置为非零值,则在使用 arg_parse 解析时添加“–version”选项,该选项会打印程序版本并以零状态退出。如果使用 ARGP_NO_HELP 标志,则情况并非如此。如果设置了 ARGP_NO_EXIT 标志,则程序的退出行为将被抑制或修改,就像其他程序将使用 argp 解析器一样。

它应该指向具有这种类型签名的函数:

void print-version (FILE *stream, struct argp_state *state)

有关状态的说明,请参阅 Argp 解析状态

此变量优先于 argp_program_version,如果程序的版本信息不易以简单字符串表示,则该变量很有用。

变量:error_t argp_err_exit_status

这是 argp 由于解析错误退出时使用的退出状态。如果用户程序未定义或设置,则默认为:<sysexits.h> 中的 EX_USAGE。

2.3.3. 指定 Argp 解析器

Specifying Argp Parsers

argp_parse 函数的第一个参数是指向 struct argp 的指针,它被称为 argp 解析器:

数据类型:struct argp

此结构指定如何解析一组给定的选项和参数,可能与其他 argp 解析器一起使用。它具有以下字段:

const struct argp_option *options

指向 argp_option 结构向量的指针,指定此 argp 解析器理解哪些选项;如果根本没有选项,它可能为零。请参阅在 Argp 解析器中指定选项

argp_parser_t parser

指向定义此解析器操作的函数的指针;它为每个解析的选项调用,并在解析过程中的其他明确定义的点处调用。零值与指向始终返回 ARGP_ERR_UNKNOWN 的函数的指针相同。请参阅 Argp 解析器函数

const char *args_doc

如果非零,则描述此解析器调用哪些非选项参数的字符串。这仅用于打印“Usage:”消息。如果它包含换行符,则由它们分隔的字符串被视为替代使用模式并打印在单独的行上。第一行之后的行以‘or:’而不是‘Usage:’作为前缀。

const char *doc

如果非零,则包含要在长帮助消息中的选项之前和之后打印的额外文本的字符串,两个部分由垂直制表符(‘\v’,‘\013’)字符分隔。按照惯例,选项之前的文档只是解释程序功能的简短字符串。在选项之后打印的文档更详细地描述了行为。

const struct argp_child *children

指向 argp_child 结构向量的指针。此指针指定哪些附加 argp 解析器应与此组合。请参阅组合多个 Argp 解析器

char *(*help_filter)(int key, const char *text, void *input)

如果非零,则指向过滤帮助消息输出的函数的指针。请参阅自定义 Argp 帮助输出

const char *argp_domain

如果非零,则 argp 库中使用的字符串将使用此字符串描述的域进行转换。如果为零,则使用当前默认域。

在上述组中,通常只需要 options、parser、args_doc 和 doc 字段。如果将 argp 解析器定义为初始化的 C 变量,则只需在初始化程序中指定使用的字段。由于 C 结构初始化的工作方式,其余部分将默认为零。这种设计在大多数 argp 结构中都得到了利用;最常用的字段在开头附近分组,未使用的字段未指定。

2.3.4. 在 Argp 解析器中指定选项

Specifying Options in an Argp Parser

struct argp 中的 options 字段指向 struct argp_option 结构的向量,每个结构指定 argp 解析器支持的选项。一个选项可以使用多个条目,前提是它有多个名称。这应该由所有字段中都为零的条目终止。请注意,当使用初始化的 C 数组作为选项时,写入 { 0 } 就足以实现这一点。

数据类型:struct argp_option

此结构指定 argp 解析器可以理解的单个选项,以及如何解析和记录该选项。它具有以下字段:

const char *name

该选项的长名称,对应长选项‘–name’;如果此选项只有一个短名称,则此字段可能为零。要为一个选项指定多个名称,可以在此之后添加其他条目,并设置 OPTION_ALIAS 标志。请参阅 Argp 选项的标志

int key

当前选项提供给选项解析器的整数键。如果 key 的值是可打印的 ASCII 字符(即 isascii (key) 为 true),它还指定一个短选项“-char”,其中 char 是带有代码键的 ASCII 字符。

const char *arg

如果非零,这是与此选项关联的参数的名称,必须提供(例如,使用 ‘–name=value’ 或 ‘-char value’ 语法),除非 OPTION_ARG_OPTIONAL 标志(请参阅标志Argp 选项)已设置,在这种情况下可以提供。

int flags

与此选项相关的标志,其中一些在上面提到过。请参阅 Argp 选项的标志。

const char *doc

此选项的文档字符串,用于在帮助消息中打印。

如果 name 和 key 字段都为零,则此字符串将在正常选项列的左侧以标签形式打印,使其可用作组标题。这将是其组中打印的第一件事。在这种用法中,通常以“:”字符结束字符串。

int group

此选项的组标识。

在长帮助消息中,选项在每个组中按字母顺序排序,并且组以 0、1、2、…、n、-m、…、-2、-1 的顺序显示。

具有此字段 0 的选项数组中的每个条目都将继承前一个条目的组号,如果是第一个则为零。如果它是名称和键字段均为零的组头,则上一个条目 + 1 是默认值。诸如“–help”之类的自动选项被放入组-1。

请注意,由于 C 结构初始化规则,该字段通常不需要指定,因为 0 是正确的值。

2.3.4.1. Argp 选项的标志

Flags for Argp Options

以下标志可能一起出现在结构 argp_option 的标志字段中。这些标志控制该选项如何在帮助消息中解析或显示的各个方面:

OPTION_ARG_OPTIONAL

与此选项关联的参数是可选的。

OPTION_HIDDEN

此选项不会显示在任何帮助消息中。

OPTION_ALIAS

此选项是最接近的先前非别名选项的别名。这意味着它将显示在同一个帮助条目中,并且将从被别名的选项中继承名称和键以外的字段。

OPTION_DOC

这个选项实际上不是一个选项,应该被实际的选项解析器忽略。它是文档的任意部分,应该以与选项大致相同的方式显示。这称为文档选项。

如果设置了此标志,则选项名称字段将在通常显示短选项的左边距处不加修改地显示(例如,未添加“–”前缀),并且此文档字符串保留在其通常的位置.出于排序的目的,任何前导空格和标点符号都将被忽略,除非第一个非空格字符是“-”。此条目显示在同一组中的所有选项之后,在带有前导“-”的 OPTION_DOC 条目之后。

OPTION_NO_USAGE

此选项不应包含在“长期”使用消息中,但仍应包含在其他帮助消息中。这适用于在 argp 的 args_doc 字段中完整记录的选项。请参阅指定 Argp 解析器。在通用用法列表中包含这个选项是多余的,应该避免。

例如,如果 args_doc 是“FOO BAR\n-x BLAH”,而“-x”选项的目的是区分这两种情况,那么“-x”可能应该被标记为 OPTION_NO_USAGE。

2.3.5. Argp 解析器函数

Argp Parser Functions

结构 argp 中的解析器字段指向的函数(请参阅指定 Argp 解析器)定义了响应解析的每个选项或参数时发生的操作。它也被用作一个钩子,允许解析器在解析期间在某些其他点执行任务。

Argp 解析器函数具有以下类型签名:

error_t parser (int key, char *arg, struct argp_state *state)

其中参数如下:

key

对于每个被解析的选项,解析器被调用,并使用选项向量中该选项的键字段中的键值。请参阅在 Argp 解析器中指定选项。解析器在其他时候也使用特殊的保留键调用,例如用于非选项参数的 ARGP_KEY_ARG。请参阅 Argp 解析器函数的特殊键

arg

如果 key 是一个选项,则 arg 是它的给定值。如果未指定值,则默认为零。只有具有非零 arg 字段的选项才能具有值。除非指定了 OPTION_ARG_OPTIONAL 标志,否则这些必须始终具有值。如果正在解析的输入为不允许的选项指定了一个值,则在调用解析器之前会导致错误。

如果 key 是 ARGP_KEY_ARG,则 arg 是非选项参数。其他特殊键始终具有零参数。

state

state 指向一个结构 argp_state,包含有关当前解析状态的有用信息,供解析器使用。请参阅 Argp 解析状态

当解析器被调用时,它应该执行任何适合 key 的操作,成功返回 0,如果 key 的值没有被这个解析器函数处理,则返回 ARGP_ERR_UNKNOWN,如果发生真正的错误,则返回 unix 错误代码。请参阅错误代码

宏:int ARGP_ERR_UNKNOWN

Argp 解析器函数应该为它们无法识别的任何键值返回 ARGP_ERR_UNKNOWN,或者对于它们无法处理的非选项参数 (key == ARGP_KEY_ARG)。

一个典型的解析器函数在 key 上使用 switch 语句:

error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
  switch (key)
    {
    case option_key:
      action
      break;default:
      return ARGP_ERR_UNKNOWN;
    }
  return 0;
}
2.3.5.1. Argp 解析器函数的特殊键

Special Keys for Argp Parser Functions

除了与用户选项对应的键值之外,argp 解析器函数的键参数可能还有许多其他特殊值。在以下示例中,arg 和 state 指的是解析器函数参数。请参阅 Argp 解析器函数

ARGP_KEY_ARG

这根本不是一个选项,而是一个命令行参数,其值由 arg 指向。

当由于组合了 argp 解析器而存在多个解析器函数时,不可能知道哪个解析器将处理特定参数。每个都被调用,直到一个返回 0 或除 ARGP_ERR_UNKNOWN 之外的错误;如果未处理参数,则 argp_parse 立即返回成功,不再解析任何参数。

一旦解析器函数对该键返回成功,该事实就会被记录下来,并且不会使用 ARGP_KEY_NO_ARGS 情况。但是,如果在处理参数时解析器函数减少其状态参数的下一个字段,则该选项不会被视为已处理;这是为了允许您实际修改参数,也许是一个选项,并再次处理它。

ARGP_KEY_ARGS

如果解析器函数为 ARGP_KEY_ARG 返回 ARGP_ERR_UNKNOWN,则立即使用键 ARGP_KEY_ARGS 再次调用它,这具有相似的含义,但更方便使用所有剩余的参数。arg 为 0,参数向量的尾部可以在 state->argv + state->next 找到。如果此键返回成功,并且 state->next 未更改,则认为所有剩余参数已被使用。否则,state->next 的调整量表示使用了多少。这是一个使用两者的示例,用于不同的参数:

case ARGP_KEY_ARG:
  if (state->arg_num == 0)
    /* First argument */
    first_arg = arg;
  else
    /* Let the next case parse it.  */
    return ARGP_KEY_UNKNOWN;
  break;
case ARGP_KEY_ARGS:
  remaining_args = state->argv + state->next;
  num_remaining_args = state->argc - state->next;
  break;

ARGP_KEY_END

这表明没有更多的命令行参数。解析器函数以不同的顺序调用,首先是子级。这允许每个解析器为父级清理其状态。

ARGP_KEY_NO_ARGS

因为如果没有任何非选项参数通常会进行一些特殊处理,所以如果解析器函数没有成功处理任何非选项参数,则使用此键调用解析器函数。这在 ARGP_KEY_END 之前调用,其中对先前解析的参数进行更一般的有效性检查。

ARGP_KEY_INIT

这是在任何解析完成之前传入的。之后,状态的 child_input 字段的每个元素的值(如果有)被复制到每个孩子的状态,作为调用其解析器时输入的初始值。

ARGP_KEY_SUCCESS

当解析成功完成时传入,即使参数仍然存在。

ARGP_KEY_ERROR

如果发生错误并终止解析,则传入。在这种情况下,永远不会使用密钥为 ARGP_KEY_SUCCESS 进行调用。

ARGP_KEY_FINI

任何解析器都见过的最终密钥,即使在 ARGP_KEY_SUCCESS 和 ARGP_KEY_ERROR 之后。ARGP_KEY_INIT 分配的任何资源都可以在这里释放。有时,分配的某些资源将在成功解析后返回给调用者。在这种情况下,可以在 ARGP_KEY_ERROR 情况下释放这些特定资源。

在所有情况下,ARGP_KEY_INIT 是解析器函数看到的第一个键,而 ARGP_KEY_FINI 是最后一个,除非解析器为 ARGP_KEY_INIT 返回错误。其他键可以按以下顺序之一出现。opt 指的是任意选项键:

opt… ARGP_KEY_NO_ARGS ARGP_KEY_END ARGP_KEY_SUCCESS

被解析的参数不包含任何非选项参数。

( opt | ARGP_KEY_ARG )… ARGP_KEY_END ARGP_KEY_SUCCESS

所有非选项参数都由解析器函数成功处理。如果组合了多个 argp 解析器,则可能有多个解析器函数。

( opt | ARGP_KEY_ARG )… ARGP_KEY_SUCCESS

一些非选项参数无法识别。

当每个解析器函数为参数返回 ARGP_KEY_UNKNOWN 时会发生这种情况,在这种情况下,如果 arg_index 是空指针,则解析会在该参数处停止。否则会发生错误。

在所有情况下,如果将 arg_index 的非空值传递给 argp_parse,则第一个未解析的命令行参数的索引将在该值中传回。

如果发生错误并且被 argp 检测到或因为解析器函数返回错误值,则使用 ARGP_KEY_ERROR 调用每个解析器。除了使用 ARGP_KEY_FINI 进行最后一次调用外,不会再进行调用。

2.3.5.2. Argp解析状态

Argp Parsing State

argp 解析器函数的第三个参数(请参阅 Argp 解析器函数)是指向 struct argp_state 的指针,其中包含有关选项解析状态的信息。

数据类型:struct argp_state

此结构具有以下字段,可按说明进行修改:

const struct argp *const root_argp

正在解析的顶级 argp 解析器。请注意,这通常与调用程序传递给 argp_parse 的 struct argp 不同。请参阅使用 Argp 解析程序选项。它是一个内部 argp 解析器,包含由 argp_parse 本身实现的选项,例如“–help”。

int argc

char **argv

正在解析的参数向量。这可能会被修改。

int next

argv 中要解析的下一个参数的索引。这可能会被修改。

使用输入中所有剩余参数的一种方法是设置 state->next = state->argc,可能在记录下一个字段的值以查找使用的参数之后。当前选项可以通过减少该字段立即重新解析,然后修改 state->argv[state->next] 以反映应该重新检查的选项。

unsigned flags

提供给 argp_parse 的标志。这些可能会被修改,尽管某些标志可能仅在首次调用 argp_parse 时生效。请参阅 argp_parse 的标志

unsigned arg_num

在使用键参数 ARGP_KEY_ARG 调用解析函数时,这表示当前 arg 的编号,从 0 开始。在每次 ARGP_KEY_ARG 调用返回后递增。在所有其他时间,这是已处理的 ARGP_KEY_ARG 参数的数量。

int quoted

如果非零,则 argv 中第一个参数在特殊“–”参数之后的索引。这可以防止随后的任何内容被解释为选项。仅在参数解析超过这一点后才设置。

void *input

在输入参数中从 argp_parse 的调用者传入的任意指针。

void **child_inputs

这些是将传递给子解析器的值。该向量的长度将与当前解析器中的子节点数相同。每个子解析器将被赋予 state->child_inputs[i] 作为其 state->input 字段的值,其中 i 是该解析器的 children 字段中的子索引。请参阅组合多个 Argp 解析器

void *hook

供解析器函数使用。初始化为 0,否则被 argp 忽略。

char *name

打印消息时使用的名称。如果 argv[0] 不可用,则初始化为 argv[0] 或 program_invocation_name。

FILE *err_stream

FILE *out_stream

argp 打印时使用的 stdio 流。错误消息打印到 err_stream,所有其他输出,例如“–help”输出)到 out_stream。它们分别初始化为 stderr 和 stdout。请参阅标准流

void *pstate

私有的,供 argp 实现使用。

2.3.5.3. 用于 Argp 解析器的函数

Functions For Use in Argp Parsers

Argp 为 argp 的用户提供了许多可用的函数(请参阅 Argp 解析器函数),主要用于生成错误消息。它们将解析器函数的状态参数作为第一个参数。请参阅 Argp 解析状态

函数:void argp_usage (const struct argp_state *state)

Preliminary: | MT-Unsafe race:argpbuf env locale | AS-Unsafe heap i18n corrupt | AC-Unsafe mem corrupt lock | See POSIX Safety Concepts.

将 state 引用的 argp 解析器的标准使用消息输出到 state->err_stream 并以 exit (argp_err_exit_status) 终止程序。请参阅 Argp 全局变量

函数:void argp_error (const struct argp_state *state, const char *fmt, ...)

Preliminary: | MT-Unsafe race:argpbuf env locale | AS-Unsafe heap i18n corrupt | AC-Unsafe mem corrupt lock | See POSIX Safety Concepts.

打印 printf 格式字符串 fmt 和后面的 args,前面是程序名称和 ‘:’,然后是 ‘Try … --help’ 消息,并以 argp_err_exit_status 的退出状态终止程序。请参阅 Argp 全局变量。

函数:void argp_failure (const struct argp_state *state, int status, int errnum, const char *fmt, ...)

Preliminary: | MT-Safe | AS-Unsafe corrupt heap | AC-Unsafe lock corrupt mem | See POSIX Safety Concepts.

与标准 GNU 错误报告函数错误类似,它会打印程序名称和“:”、printf 格式字符串 fmt 以及适当的以下参数。如果它不为零,则打印 errnum 的标准 unix 错误文本。如果状态不为零,它会以该值作为其退出状态来终止程序。

argp_failure 和 argp_error 的区别在于 argp_error 用于解析错误,而 argp_failure 用于解析过程中出现但不反映输入的语法问题的其他问题,例如选项的非法值、错误的月相等.

函数:void argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags)

Preliminary: | MT-Unsafe race:argpbuf env locale | AS-Unsafe heap i18n corrupt | AC-Unsafe mem corrupt lock | See POSIX Safety Concepts.

将状态引用的 argp 解析器的帮助消息输出到流。flags 参数确定生成哪种帮助消息。请参阅 argp_help 函数的标志

错误输出发送到 state->err_stream,打印的程序名称为 state->name。

如果将 ARGP_NO_EXIT 或 ARGP_NO_ERRS 标志传递给 argp_parse,则这些函数的输出或程序终止行为可能会被抑制。请参阅 argp_parse 的标志

如果 argp 解析器被导出以供其他程序使用(例如,由库),则此行为很有用,并且可以在不希望终止程序以响应解析错误的上下文中使用。在用于此类一般用途的 argp 解析器中,以及在程序不终止的情况下,对这些函数中的任何一个的调用都应跟随返回适当错误代码的代码:

if (bad argument syntax)
  {
     argp_usage (state);
     return EINVAL;
  }

如果仅在未设置 ARGP_NO_EXIT 时才使用解析器函数,则可以省略返回。

2.3.6. 组合多个 Argp 解析器

Combining Multiple Argp Parsers

struct argp 中的 children 字段使其他 argp 解析器能够与引用的解析器组合以解析一组参数。该字段应指向 struct argp_child 的向量,该向量由 argp 字段中值为零的条目终止。

如果组合解析器之间出现冲突,例如当两个指定具有相同名称的选项时,解析器冲突会以有利于父 argp 解析器或子列表中较早的 argp 解析器的方式解决。

数据类型:struct argp_child

结构 argp 中的 children 字段指向的辅助 argp 解析器列表中的一个条目。字段如下:

const struct argp *argp

子 argp 解析器,或从零到列表末尾。

int flags

这个孩子的标志。

const char *header

如果非零,这是一个可选标题,在子选项之前的帮助输出中打印。作为副作用,非零值会强制将子选项组合在一起。要在不实际打印标题字符串的情况下实现此效果,请使用值“”。与选项条目中指定的标题字符串一样,最后一个字符的常规值是“:”。请参阅在 Argp 解析器中指定选项

int group

这是子选项相对于父 argp 解析器中的其他“合并”选项进行分组的地方。这些值与 struct argp_option 中的组字段相同。请参阅在 Argp 解析器中指定选项。所有子分组都遵循特定组级别的父选项。如果此字段和标题都为零,则子选项不会组合在一起,它们会在父选项组级别与父选项合并。

2.3.7. argp_parse 的标志

Flags for argp_parse

argp_parse 的默认行为旨在方便解析程序命令行参数的最常见情况。要修改这些默认值,可以在 argp_parse 的 flags 参数中一起使用以下标志:

ARGP_PARSE_ARGV0

不要忽略 argp_parse 的 argv 参数的第一个元素。除非设置了 ARGP_NO_ERRS,否则参数向量的第一个元素将被跳过以进行选项解析,因为它对应于命令行中的程序名称。

ARGP_NO_ERRS

不要将未知选项的错误消息打印到 stderr;除非设置此标志,否则 ARGP_PARSE_ARGV0 将被忽略,因为 argv[0] 用作错误消息中的程序名称。该标志暗示 ARGP_NO_EXIT。这是基于假设错误时静默退出是不良行为。

ARGP_NO_ARGS

不要解析任何非选项参数。通常,这些是通过使用 ARGP_KEY_ARG 键调用解析函数来解析的,实际参数是值。这个标志通常不需要设置,因为默认行为是一旦参数解析失败就停止解析。请参阅 Argp 解析器函数

ARGP_IN_ORDER

按照它们在命令行中出现的相同顺序解析选项和参数。通常它们会重新排列,以便所有选项都排在第一位。

ARGP_NO_HELP

不要提供标准的长选项“–help”,这通常会导致用法和选项帮助信息输出到标准输出并退出 (0)。

ARGP_NO_EXIT

不要退出错误,尽管它们仍然可能导致错误消息。

ARGP_LONG_ONLY

使用 GNU getopt ‘long-only’ 规则来解析参数。这允许仅使用单个“-”(即“-help”)来识别长选项。这导致接口的用处不大,并且不鼓励使用它,因为它与大多数 GNU 程序的工作方式以及 GNU 编码标准相冲突。

ARGP_SILENT

关闭任何消息打印/退出选项,特别是 ARGP_NO_EXIT、ARGP_NO_ERRS 和 ARGP_NO_HELP。

2.3.8. 自定义 Argp 帮助输出

Customizing Argp Help Output

struct argp 中的 help_filter 字段是一个指向函数的指针,该函数在显示帮助消息之前对其进行过滤。他们有一个函数签名,如:

char *help-filter (int key, const char *text, void *input)

其中 key 是选项中的一个键,在这种情况下 text 是该选项的帮助文本。请参阅在 Argp 解析器中指定选项。或者,可以使用名称以“ARGP_KEY_HELP_”开头的特殊键之一,描述将包含哪些其他帮助文本。请参阅 Argp 帮助过滤器功能的特殊键

如果函数保持原样,则该函数应返回文本,或使用 malloc 分配的替换字符串。这将被 argp 或零释放,它不打印任何内容。text 的值是在任何翻译完成后提供的,因此如果任何替换文本需要翻译,它将由过滤器函数完成。如果用户直接调用了 argp_help,则输入要么是提供给 argp_parse 的输入,要么为零。

2.3.8.1. Argp 帮助过滤功能的特殊键

Special Keys for Argp Help Filter Functions

除了用户选项的键值之外,以下特殊值可以作为第一个参数传递给 argp 帮助过滤器函数。他们指定 text 参数包含哪些帮助文本:

ARGP_KEY_HELP_PRE_DOC

选项前面的帮助文本。

ARGP_KEY_HELP_POST_DOC

选项后面的帮助文本。

ARGP_KEY_HELP_HEADER

选项标头字符串。

ARGP_KEY_HELP_EXTRA

这是在所有其他文档之后使用的;此键的文本为零。

ARGP_KEY_HELP_DUP_ARGS_NOTE

抑制重复选项参数时打印的解释性说明。

ARGP_KEY_HELP_ARGS_DOC

参数文档字符串;正式来自 argp 解析器的 args_doc 字段。请参阅指定 Argp 解析器

2.3.9. argp_help 函数

The argp_help Function

通常,使用 argp 的程序不需要在编写时考虑到特定的打印参数使用类型帮助消息,因为标准的“–help”选项由 argp 自动处理。可以使用 argp_usage 和 argp_error 处理典型的错误情况。请参阅用于 Argp 解析器的函数。但是,如果需要在某些上下文中打印帮助消息而不是解析程序选项,则 argp 提供 argp_help 接口。

函数:void argp_help (const struct argp *argp, FILE *stream, unsigned flags, char *name)

Preliminary: | MT-Unsafe race:argpbuf env locale | AS-Unsafe heap i18n corrupt | AC-Unsafe mem corrupt lock | See POSIX Safety Concepts.

这会为 argp 解析器 argp 输出一条帮助消息以进行流式传输。打印的消息类型将由标志确定。

任何由 argp 本身自动实现的选项,例如“–help”,都不会出现在帮助输出中;因此,如果从 argp 解析器函数中调用,最好使用 argp_state_help。请参阅用于 Argp 解析器的函数

2.3.10. argp_help 函数的标志

Flags for the argp_help Function

当调用 argp_help(参见 argp_help 函数)或 argp_state_help(参见用于 Argp 解析器的函数)时,确切的输出由 flags 参数确定。这应该由以下任何标志组成,或者一起:

ARGP_HELP_USAGE

明确列出所有选项的 unix ‘Usage:’ 消息。

ARGP_HELP_SHORT_USAGE

一个 unix ‘Usage:’ 消息,显示适当的占位符以指示选项的位置;对于显示非选项参数语法很有用。

ARGP_HELP_SEE

“尝试……寻求更多帮助”信息;“…”包含程序名称和“–help”。

ARGP_HELP_LONG

详细的选项帮助消息,提供每个可用选项及其文档字符串。

ARGP_HELP_PRE_DOC

详细选项帮助之前的 argp 解析器文档字符串部分。

ARGP_HELP_POST_DOC

argp 解析器文档字符串中跟在详细选项帮助后面的部分。

ARGP_HELP_DOC

(ARGP_HELP_PRE_DOC | ARGP_HELP_POST_DOC)

ARGP_HELP_BUG_ADDR

如果 argp_program_bug_address 变量包含此信息,则打印此程序的错误报告位置的消息。

ARGP_HELP_LONG_ONLY

这将修改任何输出以反映 ARGP_LONG_ONLY 模式。

以下标志仅在与 argp_state_help 一起使用时才能理解。它们控制函数是在打印输出后返回,还是终止程序:

ARGP_HELP_EXIT_ERR

这将通过退出 (argp_err_exit_status) 终止程序。

ARGP_HELP_EXIT_OK

这将使用 exit (0) 终止程序。

以下标志是用于打印标准消息的基本标志的组合:

ARGP_HELP_STD_ERR

假设已打印解析错误的错误消息,这将打印有关如何获得帮助的消息,并以错误终止程序。

ARGP_HELP_STD_USAGE

这将打印一条标准使用消息并以错误终止程序。当没有其他特定的错误消息合适或可用时使用此选项。

ARGP_HELP_STD_HELP

这将打印“–help”选项的标准响应,并成功终止程序。

2.3.11. Argp 示例

Argp Examples

这些示例程序演示了 argp 的基本用法。

2.3.11.1. 使用 Argp 的最小程序

A Minimal Program Using Argp

这可能是使用 argp 的最小程序。除了在有任何参数时给出错误消息并退出之外,它不会做太多事情,并为“–help”打印一条相当无意义的消息。

/* This is (probably) the smallest possible program that
   uses argp.  It won’t do much except give an error
   messages and exit when there are any arguments, and print
   a (rather pointless) messages for –help. */

#include <stdlib.h>
#include <argp.h>

int
main (int argc, char **argv)
{
  argp_parse (0, argc, argv, 0, 0, 0);
  exit (0);
}
2.3.11.2. 使用只有默认选项的 Argp 的程序

A Program Using Argp with Only Default Options

该程序不使用任何选项或参数,它使用 argp 以符合 GNU 标准命令行格式。

除了不提供任何参数并实现“–help”选项外,此示例还有一个“–version”选项,它将按照 GNU 标准将给定的文档字符串和错误地址放在“–help”输出中 .

变量 argp 包含参数解析器规范。向这个结构中添加字段是大多数参数传递给 argp_parse 的方式。前三个字段正常使用,但在这个小程序中没有。这里还定义了 argp 可以使用的两个全局变量,argp_program_version 和 argp_program_bug_address。它们被认为是全局变量,因为它们对于给定的程序几乎总是不变的,即使它们对各种任务使用不同的参数解析器。

/* This program doesn’t use any options or arguments, but uses
   argp to be compliant with the GNU standard command line
   format.

   In addition to making sure no arguments are given, and
   implementing a –help option, this example will have a
   –version option, and will put the given documentation string
   and bug address in the –help output, as per GNU standards.

   The variable ARGP contains the argument parser specification;
   adding fields to this structure is the way most parameters are
   passed to argp_parse (the first three fields are usually used,
   but not in this small program).  There are also two global
   variables that argp knows about defined here,
   ARGP_PROGRAM_VERSION and ARGP_PROGRAM_BUG_ADDRESS (they are
   global variables because they will almost always be constant
   for a given program, even if it uses different argument
   parsers for various tasks). */

#include <stdlib.h>
#include <argp.h>

const char *argp_program_version =
  "argp-ex2 1.0";
const char *argp_program_bug_address =
  "<bug-gnu-utils@gnu.org>";

/* Program documentation. */
static char doc[] =
  "Argp example #2 -- a pretty minimal program using argp";

/* Our argument parser.  The options, parser, and
   args_doc fields are zero because we have neither options or
   arguments; doc and argp_program_bug_address will be
   used in the output for ‘--help’, and the ‘--version’
   option will print out argp_program_version. */
static struct argp argp = { 0, 0, 0, doc };

int
main (int argc, char **argv)
{
  argp_parse (&argp, argc, argv, 0, 0, 0);
  exit (0);
}
2.3.11.3. 使用带有用户选项的 Argp 的程序

A Program Using Argp with User Options

该程序使用与示例 2 相同的功能,添加用户选项和参数。

我们现在使用 argp 中的前四个字段(请参阅指定 Argp 解析器)并将 parse_opt 指定为解析器函数。请参阅 Argp 解析器函数

请注意,在此示例中,main 使用一个结构与 parse_opt 函数进行通信,该函数将一个指针传递给 argp_parse 的输入参数。请参阅使用 Argp 解析程序选项。它由 parse_opt 通过其 state 参数中的输入字段检索。请参阅 Argp 解析状态。当然,也可以使用全局变量来代替,但是使用这样的结构会更加灵活和简洁。

/* This program uses the same features as example 2, and uses options and
   arguments.

   We now use the first four fields in ARGP, so here’s a description of them:
     OPTIONS  – A pointer to a vector of struct argp_option (see below)
     PARSER   – A function to parse a single option, called by argp
     ARGS_DOC – A string describing how the non-option arguments should look
     DOC      – A descriptive string about this program; if it contains a
                 vertical tab character (\v), the part after it will be
                 printed *following* the options

   The function PARSER takes the following arguments:
     KEY  – An integer specifying which option this is (taken
             from the KEY field in each struct argp_option), or
             a special key specifying something else; the only
             special keys we use here are ARGP_KEY_ARG, meaning
             a non-option argument, and ARGP_KEY_END, meaning
             that all arguments have been parsed
     ARG  – For an option KEY, the string value of its
             argument, or NULL if it has none
     STATE– A pointer to a struct argp_state, containing
             various useful information about the parsing state; used here
             are the INPUT field, which reflects the INPUT argument to
             argp_parse, and the ARG_NUM field, which is the number of the
             current non-option argument being parsed
   It should return either 0, meaning success, ARGP_ERR_UNKNOWN, meaning the
   given KEY wasn’t recognized, or an errno value indicating some other
   error.

   Note that in this example, main uses a structure to communicate with the
   parse_opt function, a pointer to which it passes in the INPUT argument to
   argp_parse.  Of course, it’s also possible to use global variables
   instead, but this is somewhat more flexible.

   The OPTIONS field contains a pointer to a vector of struct argp_option’s;
   that structure has the following fields (if you assign your option
   structures using array initialization like this example, unspecified
   fields will be defaulted to 0, and need not be specified):
     NAME   – The name of this option’s long option (may be zero)
     KEY    – The KEY to pass to the PARSER function when parsing this option,
               *and* the name of this option’s short option, if it is a
               printable ascii character
     ARG    – The name of this option’s argument, if any
     FLAGS  – Flags describing this option; some of them are:
                 OPTION_ARG_OPTIONAL – The argument to this option is optional
                 OPTION_ALIAS        – This option is an alias for the
                                        previous option
                 OPTION_HIDDEN       – Don’t show this option in –help output
     DOC    – A documentation string for this option, shown in –help output

   An options vector should be terminated by an option with all fields zero. */

#include <stdlib.h>
#include <argp.h>

const char *argp_program_version =
  "argp-ex3 1.0";
const char *argp_program_bug_address =
  "<bug-gnu-utils@gnu.org>";

/* Program documentation. */
static char doc[] =
  "Argp example #3 -- a program with options and arguments using argp";

/* A description of the arguments we accept. */
static char args_doc[] = "ARG1 ARG2";

/* The options we understand. */
static struct argp_option options[] = {
  {"verbose",  'v', 0,      0,  "Produce verbose output" },
  {"quiet",    'q', 0,      0,  "Don't produce any output" },
  {"silent",   's', 0,      OPTION_ALIAS },
  {"output",   'o', "FILE", 0,
   "Output to FILE instead of standard output" },
  { 0 }
};

/* Used by main to communicate with parse_opt. */
struct arguments
{
  char *args[2];                /* arg1 & arg2 */
  int silent, verbose;
  char *output_file;
};

/* Parse a single option. */
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
  /* Get the input argument from argp_parse, which we
     know is a pointer to our arguments structure. */
  struct arguments *arguments = state->input;

  switch (key)
    {
    case 'q': case 's':
      arguments->silent = 1;
      break;
    case 'v':
      arguments->verbose = 1;
      break;
    case 'o':
      arguments->output_file = arg;
      break;

    case ARGP_KEY_ARG:
      if (state->arg_num >= 2)
        /* Too many arguments. */
        argp_usage (state);

      arguments->args[state->arg_num] = arg;

      break;

    case ARGP_KEY_END:
      if (state->arg_num < 2)
        /* Not enough arguments. */
        argp_usage (state);
      break;

    default:
      return ARGP_ERR_UNKNOWN;
    }
  return 0;
}

/* Our argp parser. */
static struct argp argp = { options, parse_opt, args_doc, doc };

int
main (int argc, char **argv)
{
  struct arguments arguments;

  /* Default values. */
  arguments.silent = 0;
  arguments.verbose = 0;
  arguments.output_file = "-";

  /* Parse our arguments; every option seen by parse_opt will
     be reflected in arguments. */
  argp_parse (&argp, argc, argv, 0, 0, &arguments);

  printf ("ARG1 = %s\nARG2 = %s\nOUTPUT_FILE = %s\n"
          "VERBOSE = %s\nSILENT = %s\n",
          arguments.args[0], arguments.args[1],
          arguments.output_file,
          arguments.verbose ? "yes" : "no",
          arguments.silent ? "yes" : "no");

  exit (0);
}
2.3.11.4. 使用多个组合的 Argp 解析器的程序

A Program Using Multiple Combined Argp Parsers

该程序使用与示例 3 相同的功能,但具有更多选项,并在“–help”输出中呈现更多结构。它还说明了如何在接受项目列表的程序的某个点之后“窃取”输入参数的其余部分。它还说明了键值 ARGP_KEY_NO_ARGS,仅在没有向程序提供非选项参数时才给出。请参阅 Argp 解析器函数的特殊键

对于结构化帮助输出,使用了两个函数:标题和两部分选项字符串。标头是选项向量中的条目。请参阅在 Argp 解析器中指定选项。前四个字段为零。两部分文档字符串位于变量 doc 中,它允许在选项之前和之后进行文档。请参阅指定 Argp 解析器,doc 的两个部分由垂直制表符分隔(‘\v’ 或 ‘\013’)。按照惯例,选项之前的文档是一个简短的字符串,说明程序的作用,而在任何选项之后,它更长,更详细地描述了行为。所有文档字符串都会自动填充以供输出,尽管可能会包含换行符以在特定点强制换行。此外,文档字符串被传递给 gettext 函数,以便可能翻译成当前语言环境。

/* This program uses the same features as example 3, but has more
   options, and somewhat more structure in the -help output.  It
   also shows how you can ‘steal’ the remainder of the input
   arguments past a certain point, for programs that accept a
   list of items.  It also shows the special argp KEY value
   ARGP_KEY_NO_ARGS, which is only given if no non-option
   arguments were supplied to the program.

   For structuring the help output, two features are used,
   *headers* which are entries in the options vector with the
   first four fields being zero, and a two part documentation
   string (in the variable DOC), which allows documentation both
   before and after the options; the two parts of DOC are
   separated by a vertical-tab character (’\v’, or ’\013’).  By
   convention, the documentation before the options is just a
   short string saying what the program does, and that afterwards
   is longer, describing the behavior in more detail.  All
   documentation strings are automatically filled for output,
   although newlines may be included to force a line break at a
   particular point.  All documentation strings are also passed to
   the ‘gettext’ function, for possible translation into the
   current locale. */

#include <stdlib.h>
#include <error.h>
#include <argp.h>

const char *argp_program_version =
  "argp-ex4 1.0";
const char *argp_program_bug_address =
  "<bug-gnu-utils@prep.ai.mit.edu>";

/* Program documentation. */
static char doc[] =
  "Argp example #4 -- a program with somewhat more complicated\
options\
\vThis part of the documentation comes *after* the options;\
 note that the text is automatically filled, but it's possible\
 to force a line-break, e.g.\n<-- here.";

/* A description of the arguments we accept. */
static char args_doc[] = "ARG1 [STRING...]";

/* Keys for options without short-options. */
#define OPT_ABORT  1            /* –abort */

/* The options we understand. */
static struct argp_option options[] = {
  {"verbose",  'v', 0,       0, "Produce verbose output" },
  {"quiet",    'q', 0,       0, "Don't produce any output" },
  {"silent",   's', 0,       OPTION_ALIAS },
  {"output",   'o', "FILE",  0,
   "Output to FILE instead of standard output" },

  {0,0,0,0, "The following options should be grouped together:" },
  {"repeat",   'r', "COUNT", OPTION_ARG_OPTIONAL,
   "Repeat the output COUNT (default 10) times"},
  {"abort",    OPT_ABORT, 0, 0, "Abort before showing any output"},

  { 0 }
};

/* Used by main to communicate with parse_opt. */
struct arguments
{
  char *arg1;                   /* arg1 */
  char **strings;               /* [string…] */
  int silent, verbose, abort;   /* ‘-s’, ‘-v’, ‘--abort’ */
  char *output_file;            /* file arg to ‘--output’ */
  int repeat_count;             /* count arg to ‘--repeat’ */
};

/* Parse a single option. */
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
  /* Get the input argument from argp_parse, which we
     know is a pointer to our arguments structure. */
  struct arguments *arguments = state->input;

  switch (key)
    {
    case 'q': case 's':
      arguments->silent = 1;
      break;
    case 'v':
      arguments->verbose = 1;
      break;
    case 'o':
      arguments->output_file = arg;
      break;
    case 'r':
      arguments->repeat_count = arg ? atoi (arg) : 10;
      break;
    case OPT_ABORT:
      arguments->abort = 1;
      break;

    case ARGP_KEY_NO_ARGS:
      argp_usage (state);

    case ARGP_KEY_ARG:
      /* Here we know that state->arg_num == 0, since we
         force argument parsing to end before any more arguments can
         get here. */
      arguments->arg1 = arg;

      /* Now we consume all the rest of the arguments.
         state->next is the index in state->argv of the
         next argument to be parsed, which is the first string
         we’re interested in, so we can just use
         &state->argv[state->next] as the value for
         arguments->strings.

         In addition, by setting state->next to the end
         of the arguments, we can force argp to stop parsing here and
         return. */
      arguments->strings = &state->argv[state->next];
      state->next = state->argc;

      break;

    default:
      return ARGP_ERR_UNKNOWN;
    }
  return 0;
}

/* Our argp parser. */
static struct argp argp = { options, parse_opt, args_doc, doc };

int
main (int argc, char **argv)
{
  int i, j;
  struct arguments arguments;

  /* Default values. */
  arguments.silent = 0;
  arguments.verbose = 0;
  arguments.output_file = "-";
  arguments.repeat_count = 1;
  arguments.abort = 0;

  /* Parse our arguments; every option seen by parse_opt will be
     reflected in arguments. */
  argp_parse (&argp, argc, argv, 0, 0, &arguments);

  if (arguments.abort)
    error (10, 0, "ABORTED");

  for (i = 0; i < arguments.repeat_count; i++)
    {
      printf ("ARG1 = %s\n", arguments.arg1);
      printf ("STRINGS = ");
      for (j = 0; arguments.strings[j]; j++)
        printf (j == 0 ? "%s" : ", %s", arguments.strings[j]);
      printf ("\n");
      printf ("OUTPUT_FILE = %s\nVERBOSE = %s\nSILENT = %s\n",
              arguments.output_file,
              arguments.verbose ? "yes" : "no",
              arguments.silent ? "yes" : "no");
    }

  exit (0);
}

2.3.12. Argp 用户自定义

Argp User Customization

通过将 ARGP_HELP_FMT 环境变量设置为逗号分隔的标记列表,程序用户可以在一定程度上控制 argp ‘–help’ 输出的格式。空白被忽略:

‘dup-args’

‘no-dup-args’

这些打开或关闭重复参数模式。在重复参数模式下,如果接受参数的选项有多个名称,则显示每个名称的参数。否则,仅显示第一个长选项。随后会打印一个注释,以便用户知道它也适用于其他名称。默认值为“no-dup-args”,不太一致,但更漂亮。

‘dup-args-note’

‘no-dup-args-note’

这些将启用或禁用通知用户抑制选项参数重复的注释。默认值为“dup-args-note”。

‘short-opt-col=n’

这将打印第 n 列中的第一个短选项。默认值为 2。

‘long-opt-col=n’

这将打印第 n 列中的第一个长选项。默认值为 6。

‘doc-opt-col=n’

这会在 n 列中打印“文档选项”(请参阅​​ Argp 选项的标志)。默认值为 2。

‘opt-doc-col=n’

这将打印从第 n 列开始的选项的文档。默认值为 29。

‘header-col=n’

这会将记录选项组的组标题缩进到第 n 列。默认值为 1。

‘usage-indent=n’

这会将“Usage:”消息中的续行缩进到第 n 列。默认值为 12。

‘rmargin=n’

这将在第 n 列或之前自动换行帮助输出。默认值为 79。

2.3.12.1. 子选项解析

Parsing of Suboptions

拥有单一级别的选项有时是不够的。可能有太多选项必须可用,或者一组选项密切相关。

对于这种情况,一些程序使用子选项。最突出的程序之一当然是 mount(8)。-o 选项采用一个参数,该参数本身是一个逗号分隔的选项列表。为了简化这样的代码编程,可以使用函数 getsubopt。

函数:int getsubopt (char **optionp, char *const *tokens, char **valuep)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

optionp 参数必须是指向包含要处理的字符串地址的变量的指针。当函数返回时,引用会更新为指向下一个子选项,如果没有更多可用的子选项,则指向终止字符“\0”。

tokens 参数引用包含已知子选项的字符串数组。所有字符串都必须以“\0”结尾,并且必须存储一个空指针来标记结尾。当 getsubopt 找到可能的合法子选项时,它会将其与令牌数组中的所有可用字符串进行比较,并返回字符串中的索引作为指示符。

如果子选项具有由“=”字符引入的关联值,则在 valuep 中返回指向该值的指针。字符串以“\0”结尾。如果没有参数可用 valuep 设置为空指针。通过这样做,调用者可以检查是否给出了必要的值或是否不存在意外的值。

如果令牌数组中未提及字符串中的下一个子选项,则在 valuep 中返回包含可能值的子选项的起始地址,并且函数的返回值为“-1”。

2.3.13. 子选项解析示例

Parsing of Suboptions Example

mount(8) 程序中可能出现的代码是使用 getsubopt 的完美示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int do_all;
const char *type;
int read_size;
int write_size;
int read_only;

enum
{
  RO_OPTION = 0,
  RW_OPTION,
  READ_SIZE_OPTION,
  WRITE_SIZE_OPTION,
  THE_END
};

const char *mount_opts[] =
{
  [RO_OPTION] = "ro",
  [RW_OPTION] = "rw",
  [READ_SIZE_OPTION] = "rsize",
  [WRITE_SIZE_OPTION] = "wsize",
  [THE_END] = NULL
};

int
main (int argc, char **argv)
{
  char *subopts, *value;
  int opt;

  while ((opt = getopt (argc, argv, "at:o:")) != -1)
    switch (opt)
      {
      case 'a':
        do_all = 1;
        break;
      case 't':
        type = optarg;
        break;
      case 'o':
        subopts = optarg;
        while (*subopts != '\0')
          switch (getsubopt (&subopts, mount_opts, &value))
            {
            case RO_OPTION:
              read_only = 1;
              break;
            case RW_OPTION:
              read_only = 0;
              break;
            case READ_SIZE_OPTION:
              if (value == NULL)
                abort ();
              read_size = atoi (value);
              break;
            case WRITE_SIZE_OPTION:
              if (value == NULL)
                abort ();
              write_size = atoi (value);
              break;
            default:
              /* Unknown suboption. */
              printf ("Unknown suboption `%s'\n", value);
              break;
            }
        break;
      default:
        abort ();
      }

  /* Do the real work. */

  return 0;
}

2.4. 环境变量

Environment Variables

当一个程序被执行时,它以两种方式接收有关它被调用的上下文的信息。第一种机制在其主函数中使用 argv 和 argc 参数,并在程序参数中进行了讨论。第二种机制使用环境变量并在本节中讨论。

argv 机制通常用于传递特定于正在调用的特定程序的命令行参数。另一方面,环境跟踪由许多程序共享、不经常更改且不经常使用的信息。

本节讨论的环境变量与您在 shell 中使用赋值和 export 命令设置的环境变量相同。从 shell 执行的程序从 shell 继承所有环境变量。

标准环境变量用于有关用户主目录、终端类型、当前语言环境等的信息;您可以为其他目的定义其他变量。所有具有值的环境变量的集合统称为环境。

环境变量的名称区分大小写,不能包含字符“=”。系统定义的环境变量总是大写的。

环境变量的值可以是任何可以表示为字符串的值。一个值不能包含嵌入的空字符,因为这被假定为终止字符串。

2.4.1. 环境访问

Environment Access

可以使用 getenv 函数访问环境变量的值。这是在头文件 stdlib.h 中声明的。

库应该使用secure_getenv 而不是getenv,这样它们就不会意外使用不受信任的环境变量。多线程程序中不允许修改环境变量。getenv 和secure_getenv 函数可以安全地用于多线程程序。

函数:char * getenv (const char *name)

Preliminary: | MT-Safe env | AS-Safe | AC-Safe | See POSIX Safety Concepts.

此函数返回一个字符串,该字符串是环境变量名称的值。您不得修改此字符串。在一些不使用 GNU C 库的非 Unix 系统中,它可能会被后续对 getenv 的调用覆盖(但不会被任何其他库函数覆盖)。如果未定义环境变量名称,则值为空指针。

函数:char * secure_getenv (const char *name)

Preliminary: | MT-Safe env | AS-Safe | AC-Safe | See POSIX Safety Concepts.

此函数类似于 getenv,但如果环境不受信任,它会返回一个空指针。当程序文件设置了 SUID 或 SGID 位时,就会发生这种情况。如果库是从 SUID/SGID 程序中引用的,通用库应该总是更喜欢这个函数而不是 getenv 以避免漏洞。

这个函数是一个 GNU 扩展。

函数:int putenv (char *string)

Preliminary: | MT-Unsafe const:env | AS-Unsafe heap lock | AC-Unsafe corrupt lock mem | See POSIX Safety Concepts.

putenv 函数在环境中添加或删除定义。如果字符串的形式为“name=value”,则将定义添加到环境中。否则,该字符串将被解释为环境变量的名称,并删除环境中此变量的任何定义。

如果函数成功,则返回 0。否则返回值非零,设置 errno 以指示错误。

setenv 函数的不同之处在于,作为参数字符串给出的确切字符串被放入环境中。如果用户应该在 putenv 调用之后更改字符串,这将自动反映在环境中。这也要求 string 不是一个自动变量,其范围在从环境中删除之前就已经存在。当然,这同样适用于稍后释放的动态分配变量。

该函数是扩展的 Unix 接口的一部分。您应该在包含任何标头之前定义 _XOPEN_SOURCE。

函数:int setenv (const char *name, const char *value, int replace)

Preliminary: | MT-Unsafe const:env | AS-Unsafe heap lock | AC-Unsafe corrupt lock mem | See POSIX Safety Concepts.

setenv 函数可用于向环境添加新定义。名称为 name 的条目将替换为值“name=value”。请注意,如果 value 是空字符串,这也是如此。为此,将创建一个新字符串并复制字符串名称和值。value 参数的空指针是非法的。如果环境已经包含一个带有键名的条目,则替换参数控制该操作。如果替换为零,则没有任何反应。否则,旧条目将被新条目替换。

请注意,您不能使用此功能完全删除条目。

如果函数成功,则返回 0。否则环境不变,返回值为 -1 并设置 errno。

这个函数最初是 BSD 库的一部分,但现在是 Unix 标准的一部分。

函数:int unsetenv (const char *name)

Preliminary: | MT-Unsafe const:env | AS-Unsafe lock | AC-Unsafe lock | See POSIX Safety Concepts.

使用此功能可以完全从环境中删除条目。如果环境包含具有键名的条目,则删除整个条目。当字符串的值部分为空时,调用此函数等效于调用 putenv。

如果 name 是空指针、指向空字符串或指向包含 = 字符的字符串,则该函数返回 -1。如果调用成功则返回 0。

这个函数最初是 BSD 库的一部分,但现在是 Unix 标准的一部分。但是,BSD 版本没有返回值。

还有一个功能可以修改整个环境。据说这个函数在 POSIX.9(Fortran 77 的 POSIX 绑定)中使用,所以人们应该期望它确实进入了 POSIX.1。但这从未发生过。但我们仍然将此功能作为 GNU 扩展提供,以支持编写符合标准的 Fortran 环境。

函数:int clearenv (void)

Preliminary: | MT-Unsafe const:env | AS-Unsafe heap lock | AC-Unsafe lock mem | See POSIX Safety Concepts.

clearenv 函数从环境中删除所有条目。稍后可以使用 putenv 和 setenv 再次添加新条目。

如果函数成功,则返回 0。否则返回值非零。

您可以直接处理环境对象的底层表示以向环境添加更多变量(例如,与您将要执行的另一个程序进行通信;请参阅执行文件)。

变量:char ** environ

环境表示为字符串数组。每个字符串的格式为“name=value”。字符串在环境中出现的顺序并不重要,但同一个名称不能出现多次。数组的最后一个元素是空指针。

该变量在头文件 unistd.h 中声明。

如果您只想获取环境变量的值,请使用 getenv。

Unix 系统和 GNU 系统将 environ 的初始值作为第三个参数传递给 main。请参阅程序参数

2.4.2. 标准环境变量

Standard Environment Variables

这些环境变量具有标准含义。这并不意味着它们总是存在于环境中。但如果这些变量存在,它们就具有这些含义。您不应该尝试将这些环境变量名称用于其他目的。

HOME

这是代表用户主目录或初始默认工作目录的字符串。

用户可以将 HOME 设置为任何值。如果您需要确保为特定用户获取正确的主目录,则不应使用 HOME;相反,在用户数据库中查找用户名(请参阅用户数据库)。

在大多数情况下,最好使用 HOME,正是因为这让用户可以指定值。

LOGNAME

这是用户用于登录的名称。由于环境中的值可以任意调整,因此这不是识别正在运行程序的用户的可靠方法;类似 getlogin 的函数(请参阅识别谁登录)更适合此目的。

大多数情况下,最好使用 LOGNAME,因为这让用户可以指定值。

LOGNAME

路径是用于搜索文件的目录名称序列。变量 PATH 保存用于搜索要运行的程序的路径。

execlp 和 execvp 函数(请参阅执行文件)使用此环境变量,许多 shell 和根据这些函数实现的其他实用程序也是如此。

路径的语法是由冒号分隔的目录名称序列。空字符串而不是目录名称代表当前目录(请参阅工作目录)。

此环境变量的典型值可能是如下字符串:

:/bin:/etc:/usr/bin:/usr/new/X11:/usr/new:/usr/local/bin

这意味着如果用户尝试执行名为 foo 的程序,系统将查找名为 foo、/bin/foo、/etc/foo 等的文件。存在的这些文件中的第一个是被执行的文件。

TERM

这指定了接收程序输出的终端类型。一些程序可以利用这些信息来利用特殊类型的终端支持的特殊转义序列或终端模式。例如,许多使用 termcap 库的程序(参见 Termcap 库手册中的查找)都使用 TERM 环境变量。

TZ

这指定了时区。有关此字符串的格式及其使用方式的信息,请参阅使用 TZ 指定时区

LANG

这指定了用于属性类别的默认语言环境,其中既未设置 LC_ALL 也未设置该类别的特定环境变量。有关语言环境的更多信息,请参阅语言环境和国际化

LC_ALL

如果设置了此环境变量,它将覆盖使用其他 LC_* 环境变量完成的所有语言环境的选择。在这种情况下,其他 LC_* 环境变量的值将被忽略。

LC_COLLATE

这指定了用于字符串排序的语言环境。

LC_CTYPE

这指定用于字符集和字符分类的语言环境。

LC_MESSAGES

这指定了用于打印消息和解析响应的语言环境。

LC_MONETARY

这指定了用于格式化货币值的语言环境。

LC_NUMERIC

这指定了用于格式化数字的语言环境。

LC_TIME

这指定了用于格式化日期/时间值的语言环境。

NLSPATH

这指定了 catopen 函数在其中查找消息翻译目录的目录。

_POSIX_OPTION_ORDER
如果定义了这个环境变量,它会抑制 getopt 和 argp_parse 对命令行参数的通常重新排序。请参阅程序参数语法约定

2.5. 辅助向量

Auxiliary Vector

当程序执行时,它会从操作系统接收有关其运行环境的信息。此信息的形式是键值对表,其中键来自 elf.h 中的“AT_”值集。部分数据是内核提供给libc消费的,可能通过sysconf等普通接口获取。但是,在逐个平台的基础上,可能存在无法通过任何其他方式获得的信息。

2.5.1. getauxval的定义

Definition of getauxval

函数:unsigned long int getauxval (unsigned long int type)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

该函数用于查询辅助向量中的条目。类型参数应该是 elf.h 中定义的“AT_”符号之一。如果找到匹配的条目,则返回该值;如果未找到该条目,则返回零并将 errno 设置为 ENOENT。

对于某些平台,键 AT_HWCAP 是查询运行时可用的任何指令集扩展的最简单方法。在这种情况下,将(必然)有一组特定于平台的“HWCAP_”值掩码在一起,描述了正在执行程序的 CPU 的能力。

2.6. 系统调用

System Calls

系统调用是程序对内核的服务请求。服务一般是只有内核才有权限做的事情,比如做I/O。程序员通常不需要关心系统调用,因为 GNU C 库中的函数几乎可以完成系统调用所做的所有事情。这些函数通过自己进行系统调用来工作。例如,有一个系统调用会更改文件的权限,但您不需要知道它,因为您可以使用 GNU C 库的 chmod 函数。

系统调用有时称为内核调用。

但是,有时您想显式地进行系统调用,为此,GNU C 库提供了 syscall 函数。syscall 比 chmod 等函数更难使用且可移植性较差,但比在汇编指令中编写系统调用更容易且更可移植。

当您使用系统专用的系统调用或比您正在使用的 GNU C 库更新的系统调用时,syscall 最有用。系统调用以完全通用的方式实现;该函数不知道特定系统调用的作用,也不知道它是否有效。

本节中对 syscall 的描述假定了运行 GNU C 库的各种平台上的系统调用的特定协议。该协议不是由任何强大的权威定义的,但我们也不会在这里描述它,因为任何编写系统调用的人都可能不会接受任何低于内核和 C 库源代码的东西作为它们之间接口的规范。

系统调用在 unistd.h 中声明。

函数:long int syscall (long int sysno, ...)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

syscall 执行一个通用的系统调用。

sysno 是系统调用号。每种系统调用都由一个数字标识。所有可能的系统调用号的宏都在 sys/syscall.h 中定义

其余参数依次为系统调用的参数,其含义取决于系统调用的类型。每种系统调用都有一定数量的参数,从零到五个。如果您编写的参数多于系统调用所需的参数,则右侧多余的参数将被忽略。

返回值是系统调用的返回值,除非系统调用失败。在这种情况下,syscall 返回 -1 并将 errno 设置为系统调用返回的错误代码。请注意,系统调用在成功时不会返回 -1。

如果您指定了无效的 sysno,则 syscall 返回 -1 且 errno = ENOSYS。

例子:

#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>int rc;

rc = syscall(SYS_chmod, "/etc/passwd", 0444);

if (rc == -1)
   fprintf(stderr, "chmod failed, errno = %d\n", errno);

如果所有兼容性星都对齐,则这等效于以下优选代码:

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>int rc;

rc = chmod("/etc/passwd", 0444);
if (rc == -1)
   fprintf(stderr, "chmod failed, errno = %d\n", errno);

2.7. 程序终止

Program Termination

程序终止的通常方式是简单地返回其主函数。从 main 函数返回的退出状态值用于向进程的父进程或 shell 报告信息。

程序也可以通过调用 exit 函数正常终止。

此外,程序可以通过信号终止;这在信号处理中有更详细的讨论。abort 函数产生一个终止程序的信号。

2.7.1. 正常终止

Normal Termination

当程序通过调用 exit 发出信号完成时,进程正常终止。从main返回相当于调用exit,main返回的值作为exit的参数。

函数:void exit (int status)

Preliminary: | MT-Unsafe race:exit | AS-Unsafe corrupt | AC-Unsafe corrupt lock | See POSIX Safety Concepts.

exit 函数告诉系统程序已经完成,这会导致它终止进程。

status 是程序的退出状态,它成为进程终止状态的一部分。此函数不返回。

正常终止会导致以下操作:

  • 使用 atexit 或 on_exit 函数注册的函数按其注册的相反顺序调用。这种机制允许您的应用程序指定在程序终止时执行的自己的“清理”操作。通常,这用于执行诸如将程序状态信息保存在文件中或解锁共享数据库中的锁定之类的操作。
  • 所有打开的流都被关闭,写出任何缓冲的输出数据。请参阅关闭流。此外,使用 tmpfile 功能打开的临时文件被删除;请参阅临时文件
  • _exit 被调用,终止程序。请参阅端接内部

2.7.2. 退出状态

Exit Status

当程序退出时,它可以使用退出状态向父进程返回少量有关终止原因的信息。这是一个介于 0 和 255 之间的值,退出进程作为参数传递给退出。

通常,您应该使用退出状态来报告有关成功或失败的非常广泛的信息。您无法提供有关失败原因的大量详细信息,而且大多数父进程无论如何都不需要太多详细信息。

对于某些程序应该返回什么样的状态值,有一些约定。最常见的约定是 0 表示成功,1 表示失败。执行比较的程序使用不同的约定:它们使用状态 1 表示不匹配,使用状态 2 表示无法比较。如果现有约定对它有意义,您的程序应该遵循现有约定。

一般约定为特殊目的保留状态值 128 及以上。特别地,值 128 用于指示在子进程中执行另一个程序失败。这个约定并不是普遍遵守的,但是在你的程序中遵循它是一个好主意。

警告:不要尝试使用错误数作为退出状态。这实际上不是很有用;父进程通常不会关心发生了多少错误。更糟糕的是,它不起作用,因为状态值被截断为八位。因此,如果程序尝试报告 256 个错误,则父程序将收到 0 个错误的报告——即成功。

出于同样的原因,使用 errno 的值作为退出状态是行不通的——这些值可以超过 255。

可移植性说明:一些非 POSIX 系统对退出状态值使用不同的约定。为了获得更大的可移植性,您可以使用宏 EXIT_SUCCESS 和 EXIT_FAILURE 分别作为成功和失败的常规状态值。它们在文件 stdlib.h 中声明。

宏:int EXIT_SUCCESS

此宏可与 exit 函数一起使用,以指示程序成功完成。

在 POSIX 系统上,此宏的值为 0。在其他系统上,该值可能是某个其他(可能是非常数)整数表达式。

宏:int EXIT_FAILURE

这个宏可以和exit函数一起使用,在一般意义上表示程序完成不成功。

在 POSIX 系统上,该宏的值为 1。在其他系统上,该值可能是某个其他(可能是非常数)整数表达式。其他非零状态值也表示失败。某些程序使用不同的非零状态值来指示特定类型的“不成功”。例如,diff 使用状态值 1 表示文件不同,使用 2 或更大表示打开文件有困难。

不要将程序的退出状态与进程的终止状态混淆。除了让程序完成之外,还有很多方法可以终止进程。但是,如果进程终止是由程序终止(即退出)引起的,则程序的退出状态将成为进程终止状态的一部分。

2.7.3. 退出时的清理

Cleanups on Exit

如果发生正常终止,您的程序可以安排运行自己的清理功能。如果您正在编写一个用于各种应用程序的库,那么坚持所有应用程序在退出之前显式调用该库的清理函数是不可靠的。通过使用 atexit 或 on_exit 在库本身中设置清理函数,使清理对应用程序不可见会更加健壮。

函数:int atexit (void (*function) (void))

Preliminary: | MT-Safe | AS-Unsafe heap lock | AC-Unsafe lock mem | See POSIX Safety Concepts.

atexit 函数注册要在正常程序终止时调用的函数函数。该函数在没有参数的情况下被调用。

atexit 的返回值在成功时为零,如果函数无法注册,则为非零。

函数:int on_exit (void (*function)(int status, void *arg), void *arg)

Preliminary: | MT-Safe | AS-Unsafe heap lock | AC-Unsafe lock mem | See POSIX Safety Concepts.

这个函数是atexit 的一个更强大的变体。它接受两个参数,一个函数函数和一个任意指针 arg。在正常程序终止时,使用两个参数调用该函数:传递给 exit 的状态值和 arg。

此函数包含在 GNU C 库中只是为了与 SunOS 兼容,其他实现可能不支持。

这是一个简单的程序,说明了 exit 和 atexit 的使用:

#include <stdio.h>
#include <stdlib.h>

void
bye (void)
{
  puts ("Goodbye, cruel world....");
}

int
main (void)
{
  atexit (bye);
  exit (EXIT_SUCCESS);
}

当这个程序被执行时,它只是打印消息并退出。

2.7.4. 中止程序

Aborting a Program

您可以使用 abort 函数中止程序。此函数的原型位于 stdlib.h 中。

函数:void abort (void)

Preliminary: | MT-Safe | AS-Unsafe corrupt | AC-Unsafe lock corrupt | See POSIX Safety Concepts.

abort 函数导致程序异常终止。这不会执行使用 atexit 或 on_exit 注册的清理函数。

这个函数实际上是通过发出一个 SIGABRT 信号来终止进程,你的程序可以包含一个处理程序来拦截这个信号;请参阅信号处理

2.7.5. 终端内部

Termination Internals

_exit 函数是用于通过退出终止进程的原语。它在头文件 unistd.h 中声明。

函数:void _exit (int status)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

_exit 函数是导致进程以状态状态终止的原语。调用此函数不会执行使用 atexit 或 on_exit 注册的清理函数。

函数:void _Exit (int status)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

_Exit 函数是 ISO C 等价于 _exit。ISO C 委员会成员不确定 _exit 和 _Exit 的定义是否兼容,因此他们没有使用 POSIX 名称。

此函数在 ISO C99 中引入并在 stdlib.h 中声明。

当一个进程因任何原因终止时——无论是因为程序终止,还是由于信号——会发生以下事情:

  • 进程中所有打开的文件描述符都被关闭。请参阅底层输入/输出。请注意,当进程终止时,流不会自动刷新;请参阅输入/输出流
  • 保存一个进程退出状态,通过 wait 或 waitpid 报告给父进程;请参阅进程完成。如果程序退出,此状态包括程序退出状态作为其低 8 位。
  • 被终止进程的任何子进程都被分配一个新的父进程。(在大多数系统上,包括 GNU,这是 init 进程,进程 ID 为 1。)
  • 一个 SIGCHLD 信号被发送到父进程。
  • 如果进程是具有控制终端的会话领导者,则向前台作业中的每个进程发送一个 SIGHUP 信号,并且控制终端与该会话解除关联。请参阅作业控制
  • 如果一个进程的终止导致一个进程组成为孤立的,并且该进程组的任何成员都停止了,则向组中的每个进程发送一个 SIGHUP 信号和一个 SIGCONT 信号。请参阅作业控制。

3. 参考

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值