如何在C中打印错误号的符号名?

如何在C中打印错误号的符号名?,c,printf,errno,strerror,C,Printf,Errno,Strerror,我可以使用perror()或strerror()打印属于errno的“人类可读”错误消息,但如果我还想打印errno的符号名(例如“EAGAIN”),该怎么办 有没有方便的函数或宏来实现这一点 更新:根据以下公认答案及其注释,附上我最终编写的代码: #include <ctype.h> #include <errno.h> #include <stdio.h> int get_errno_name(char *buf, int buf_size) {

我可以使用
perror()
strerror()
打印属于
errno
的“人类可读”错误消息,但如果我还想打印
errno
的符号名(例如“
EAGAIN
”),该怎么办

有没有方便的函数或宏来实现这一点

更新:根据以下公认答案及其注释,附上我最终编写的代码:

#include <ctype.h>
#include <errno.h>
#include <stdio.h>

int get_errno_name(char *buf, int buf_size) {
    // Using the linux-only gawk instead of awk, because of the convenient
    // match() functionality. For Posix portability, use a different recipe...
    char cmd[] = "e=       && " // errno to be inserted here (max digits = 6)
                 "echo '#include <errno.h>' | "
                 "gcc -dM -E - | " // optionally, use $CC inead of gcc
                 "gawk \"match(\\$0, /^#[[:space:]]*define[[:space:]]+"
                     "(E[[:alnum:]]+)[[:space:]]+$e($|[^[:alnum:]])/, m) "
                     "{ print m[1] }\"";
    {
        // Insert the errno as the "e" shell variable in the command above.
        int errno_digit_c = snprintf(cmd + 2, 6, "%d", errno);
        if (errno_digit_c < 1) {
            fprintf(stderr, "Failed to stringify an errno "
                            "in get_errno_name().\n");
            return -1;
        }
        // Replace the inserted terminating '\0' with whitespace
        cmd[errno_digit_c + 2] = ' ';
    }
    FILE *f = popen(cmd, "r");
    if (f == NULL) {
        perror("Failed to popen() in get_errno_name()");
        return -1;
    }
    int read_size = 0, c;
    while ((c = getc(f)) != EOF) {
        if (isalnum(c)) {
            buf[read_size++] = c;
            if (read_size + 1 > buf_size) {
                fprintf(stderr, "Read errno name is larger than the character "
                                "buffer supplied to get_errno_name().\n");
                return -1;
            }
        }
    }
    buf[read_size++] = '\0';
    if (pclose(f) == -1) {
        perror("Failed to pclose() in get_errno_name()");
        return -1;
    }
    return read_size;
}
#包括
#包括
#包括
int get_errno_名称(字符*buf,int buf_大小){
//使用linux only gawk而不是awk,因为
//match()功能。为了Posix的可移植性,请使用不同的配方。。。
char cmd[]=“e=&&&”//此处插入的错误号(最大位数=6)
“回显“#包括”|”
“gcc-dM-E-|”//可选地,在gcc之外使用$CC
“gawk\”匹配(\\$0,/^\[:space:][]*定义[:space:][]+”
(E[:alnum:]+[:space:]+$E($|[^[:alnum:]])/,m)
“{print m[1]}\”;
{
//在上面的命令中插入errno作为“e”shell变量。
int errno_digit_c=snprintf(cmd+2,6,“%d”,errno);
如果(错误数字c<1){
fprintf(stderr,“无法字符串化错误号”
“在get_errno_name()中。\n”);
返回-1;
}
//将插入的终止“\0”替换为空白
cmd[errno_digital_c+2]='';
}
文件*f=popen(cmd,“r”);
如果(f==NULL){
perror(“未能在get\u errno\u name()中打开()”;
返回-1;
}
int read_size=0,c;
而((c=getc(f))!=EOF){
if(isalnum(c)){
buf[read_size++]=c;
如果(读取大小+1>buf大小){
fprintf(stderr,“读取错误没有名称大于字符”
“已提供缓冲区以获取_errno_name()。\n”);
返回-1;
}
}
}
buf[read_size++]='\0';
如果(pclose(f)=-1){
perror(“未能在get\u errno\u name()中关闭()”;
返回-1;
}
返回读取大小;
}

据我所知,你不能。某些整数错误常量映射到多个符号名,例如EAGAIN和EWOULDBLOCK。显然,您可以在设置errno的命令的手册页上查找它们。

我不知道有哪个函数可以做到这一点,但是如果您熟悉程序中返回的错误,编写一个函数并不困难


您可以编写包含strerror()输出的字符串,并将for循环和if语句与strcmp()一起使用(它根据成功或失败返回一个值)来比较您编写的字符串。如果它们相同,则可以将符号名*设置为另一个字符串来输出。

如果您知道预期的错误,则可以使用以下样式针对
errno
编写大的
开关/case
If/else
块:

if (errno == EAGAIN)
    fprintf(stderr, "EAGAIN");

明显的问题是,如果您想要特定的errno“name”,您需要针对每个可能的选项进行写入,而且有很多。

因为符号名存储为枚举,而
C
将它们视为
整数。您必须创建一个类似于此问题的函数。您可能很容易将其缩短为一个宏,但必须为每个错误创建一个案例。

没有简单的方法可以做到这一点

您可以创建一个程序——我已经创建了一个程序,可以作为库函数重新打包——它可以从数字转换为名称。但生成表有一定难度。我使用一个运行编译器(GCC或等效程序)的Perl脚本,该脚本带有选项(
-H
)来列出包含在include
/usr/include/errno.H
中的头文件,然后扫描这些文件,查找名称(
#define
plus
E
,后跟大写字母或数字)、数字和注释。这适用于Linux、Mac OS X、Solaris、HP-UX和AIX。这并不是特别琐碎

注意,Mac OS X上的
errno.h
包含一个名称
ELAST
(一个保留给实现使用的名称),它是最高数字的副本(但映射会随着版本的变化而变化;我相信Mountain Lion是105,而Mavericks是106)

不幸的是,目前还没有标准的API来实现这一点,据我所知,没有其他开源C库可以实现这一点

如其他答案所示,唯一的“困难”部分是获取
errno
名称和值列表以进行查找,并彻底处理多个
errno
名称可能是同一
errno
编号的名称的所有情况

因此,在
errnoname
库中,我将其作为一个独立的步骤处理,您不必处理,它将许多操作系统的
errno
名称收集到一个列表中,然后从中生成实际发布的C代码,因此,最终的结果就是简单高效的C语言,可以编译和运行它,而不需要依赖于运行外部命令或查找头文件

无论如何,由于它是在“零条款BSD许可证”(0BSD)下发布的,这是一个许可证,或者更准确地说是一个公共域等效许可证,因此您可以用它做任何您想做的事情

下面是我的库中函数的直接复制粘贴,这样这个答案就可以独立了。几点注意:

  • 这涵盖了从2019年8月开始,我能找到的Linux、Darwin(Mac OS X和iOS X)、FreeBSD、NetBSD、OpenBSD、DragonflyBSD和几个封闭源代码Unix的所有
    errno
    名称

  • 如果给它一个
    e,它将返回一个
    NULL
    指针