将POSIX整数errno转换为编译时常量

8
有时候(例如使用 stracegdb 等工具),会发现 POSIX 调用将 errno 设置为整数值,而且想要知道编译时的 C 常量(更准确地说是预处理器定义)来检查它(如 ECHILD) - 请参见例如waitpid for child process not succeeding
例如,在上述链接的问题中,整数错误号码 10 返回到了 errno 中。我想从中得到字符串 ECHILD,而不是像 perrorstrerror 给出的内容(类似于“没有子进程”等)。
这里是明显但行不通的方法:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main (int argc, char **argv)
{
  printf ("%s\n", strerror (10));
  exit (0);
}

这将打印输出:

No child processes

不是ECHILD,因此无法执行所需操作。

有没有比在/usr/include中进行相对手动的grep更简单的方法?

你可能认为是重复项但实际上并非如此:

适当的答案可能涉及一些魔法,以预处理 /usr/include 的适当部分,并显示以 E 开头且具有适当值的任何常量。


问题有点不清楚。您的意思是,给定ECHILD的int值,您想要该符号“ECHILD”的名称吗? - BadZen
@BadZen - 没错。我会澄清一下。 - abligh
@cdarke - 我认为那个跟我提到的第二个差不多 - 我不需要 用C语言 来实现它。 - abligh
11个回答

6
您可以直接调用C预处理器。对于GCC工具链,预处理器可执行文件是 cpp
编辑:我意识到您特别提到了POSIX,而此示例特定于GCC,但也许这是一个开始)
以下是我为您的情况想出的一个示例:
$ cpp -dM -include /usr/include/errno.h | grep '^#define E' | sed 's/^#define \(E[A-Z0-9]*\)\s*\(.*\)$/\2 \1/g' | sort -n
EAGAIN EWOULDBLOCK
EDEADLK EDEADLOCK
EOPNOTSUPP ENOTSUP
1 EPERM
2 ENOENT
3 ESRCH
4 EINTR
5 EIO
6 ENXIO
7 E2BIG
8 ENOEXEC
9 EBADF
10 ECHILD
11 EAGAIN
12 ENOMEM
13 EACCES
14 EFAULT
15 ENOTBLK
16 EBUSY
17 EEXIST
18 EXDEV
19 ENODEV
20 ENOTDIR
21 EISDIR
22 EINVAL
23 ENFILE
24 EMFILE
25 ENOTTY
26 ETXTBSY
27 EFBIG
28 ENOSPC
29 ESPIPE
30 EROFS
31 EMLINK
32 EPIPE
33 EDOM
34 ERANGE
35 EDEADLK
36 ENAMETOOLONG
37 ENOLCK
38 ENOSYS
39 ENOTEMPTY
40 ELOOP
42 ENOMSG
43 EIDRM
44 ECHRNG
45 EL2NSYNC
46 EL3HLT
47 EL3RST
48 ELNRNG
49 EUNATCH
50 ENOCSI
51 EL2HLT
52 EBADE
53 EBADR
54 EXFULL
55 ENOANO
56 EBADRQC
57 EBADSLT
59 EBFONT
60 ENOSTR
61 ENODATA
62 ETIME
63 ENOSR
64 ENONET
65 ENOPKG
66 EREMOTE
67 ENOLINK
68 EADV
69 ESRMNT
70 ECOMM
71 EPROTO
72 EMULTIHOP
73 EDOTDOT
74 EBADMSG
75 EOVERFLOW
76 ENOTUNIQ
77 EBADFD
78 EREMCHG
79 ELIBACC
80 ELIBBAD
81 ELIBSCN
82 ELIBMAX
83 ELIBEXEC
84 EILSEQ
85 ERESTART
86 ESTRPIPE
87 EUSERS
88 ENOTSOCK
89 EDESTADDRREQ
90 EMSGSIZE
91 EPROTOTYPE
92 ENOPROTOOPT
93 EPROTONOSUPPORT
94 ESOCKTNOSUPPORT
95 EOPNOTSUPP
96 EPFNOSUPPORT
97 EAFNOSUPPORT
98 EADDRINUSE
99 EADDRNOTAVAIL
100 ENETDOWN
101 ENETUNREACH
102 ENETRESET
103 ECONNABORTED
104 ECONNRESET
105 ENOBUFS
106 EISCONN
107 ENOTCONN
108 ESHUTDOWN
109 ETOOMANYREFS
110 ETIMEDOUT
111 ECONNREFUSED
112 EHOSTDOWN
113 EHOSTUNREACH
114 EALREADY
115 EINPROGRESS
116 ESTALE
117 EUCLEAN
118 ENOTNAM
119 ENAVAIL
120 EISNAM
121 EREMOTEIO
122 EDQUOT
123 ENOMEDIUM
124 EMEDIUMTYPE
125 ECANCELED
126 ENOKEY
127 EKEYEXPIRED
128 EKEYREVOKED
129 EKEYREJECTED
130 EOWNERDEAD
131 ENOTRECOVERABLE
132 ERFKILL
133 EHWPOISON

需要注意的几点:

  • 这几乎肯定并不完美。很可能会漏掉一些内容或者出现错误。例如,如果在任何一个被errno.h直接或间接地#include的文件中还有其他的宏定义,那么grep '^#define E'就有可能无法从cpp命令的输出中过滤出所需的errno定义。而这绝非这种方法可能失败的唯一方式。
  • 显然,有一些情况下(比如#define EWOULDBLOCK EAGAIN),一个Exxxx值被定义为另一个之前已定义的值的同义词。
  • 在这个序列中似乎有一些缺失的数值,比如41。我不确定这是正常的还是这种方法遗漏了一些东西的例子。

尽管存在这些免责声明,但我们可以将这种方法用作脚本的基础(你可以选择从Makefile中调用它),以自动生成查找表(例如,errno-lookup.c),并在你的代码中引用该表,以获取任何给定errno 值的相关符号。


2
<errno.h>中定义的EFOO常量集及其值在不同系统中会有所不同,且通常会有多个EFOO常量拥有相同的值。例如,在我的系统上,EAGAINEWOULDBLOCK都具有值11,因此对于给定的数字errno值,并不存在唯一的EFOO符号。
我已经从各个系统收集了160个E*符号的列表,但这并不是最终的或全面的。您可以编写一个脚本,将该列表作为输入,并生成一个C源程序,用于打印每个符号的数值。对于每个符号,该程序将包含以下内容:
#ifdef E2BIG
    printf("E2BIG %d\n", E2BIG);
#endif

有了这个,你可以生成一个返回相应符号作为字符串的C函数,或者如果没有这样的符号,返回类似于"?"的东西。

我知道这不是一个完整的答案,但这是一个很好的起点。我可能会稍后实现更完整的解决方案。如果这样,我可能会创建一个Github项目,并在此答案中更新链接。

以下是列表:

E2BIG EACCES EADDRINUSE EADDRNOTAVAIL EADV EAFNOSUPPORT EAGAIN
EALREADY EBADCOOKIE EBADE EBADF EBADFD EBADHANDLE EBADMSG EBADR EBADRQC
EBADSLT EBADTYPE EBFONT EBUSY ECANCELED ECANCELLED ECHILD ECHRNG ECOMM
ECONNABORTED ECONNREFUSED ECONNRESET EDEADLK EDEADLOCK EDESTADDRREQ
EDOM EDOTDOT EDQUOT EEXIST EFAULT EFBIG EHOSTDOWN EHOSTUNREACH EHWPOISON
EIDRM EILSEQ EINIT EINPROGRESS EINTR EINVAL EIO EIOCBQUEUED EIOCBRETRY
EISCONN EISDIR EISNAM EJUKEBOX EKEYEXPIRED EKEYREJECTED EKEYREVOKED
EL2HLT EL2NSYNC EL3HLT EL3RST ELIBACC ELIBBAD ELIBEXEC ELIBMAX ELIBSCN
ELNRNG ELOCKUNMAPPED ELOOP EMAXERRNO EMEDIUMTYPE EMFILE EMLINK EMSGSIZE
EMULTIHOP ENAMETOOLONG ENAVAIL ENETDOWN ENETRESET ENETUNREACH ENFILE
ENOANO ENOBUFS ENOCSI ENODATA ENODEV ENOENT ENOEXEC ENOIOCTLCMD ENOKEY
ENOLCK ENOLINK ENOMEDIUM ENOMEM ENOMSG ENONET ENOPKG ENOPROTOOPT ENOSPC
ENOSR ENOSTR ENOSYM ENOSYS ENOTACTIVE ENOTBLK ENOTCONN ENOTDIR ENOTEMPTY
ENOTNAM ENOTRECOVERABLE ENOTSOCK ENOTSUP ENOTSUPP ENOTSYNC ENOTTY
ENOTUNIQ ENXIO EOPNOTSUPP EOVERFLOW EOWNERDEAD EPERM EPFNOSUPPORT EPIPE
EPROCLIM EPROTO EPROTONOSUPPORT EPROTOTYPE ERANGE EREFUSED EREMCHG EREMDEV
EREMOTE EREMOTEIO EREMOTERELEASE ERESTART ERESTARTNOHAND ERESTARTNOINTR
ERESTARTSYS ERESTART_RESTARTBLOCK ERFKILL EROFS ERREMOTE ESERVERFAULT
ESHUTDOWN ESOCKTNOSUPPORT ESPIPE ESRCH ESRMNT ESTALE ESTRPIPE ETIME
ETIMEDOUT ETOOMANYREFS ETOOSMALL ETXTBSY EUCLEAN EUNATCH EUSERS
EWOULDBLOCK EXDEV EXFULL

2

我最近写了errnoname来实现这个功能。

所以你可以从问题中的示例程序中,用#include "errnoname.h"替换strerrorerrnoname,并使用errnoname.c进行编译/链接。

不幸的是,目前仍然没有标准API来实现这一点。(GNU的glibc有strerrorname_np,但其他C库目前都没有提供它,因此它甚至在所有Linux发行版上都不可用,更别说其他地方了。)

正如其他答案所表明的那样,errnoname函数本身并不难或复杂,只是手动实现非常繁琐,需要确保包含所有errno名称,并处理所有小的边角情况。

例如:

  • 正如另一个答案所指出的那样,有一些成对的错误码,例如EWOULDBLOCK等于EAGAIN,在某些系统上具有相同的值,在其他系统上则具有不同的值,而
  • errno名称根本不能保证是连续的(因此我建议将其实现为switch语句而不是数组,因为在某些罕见或未来的系统上,它可能是一个非常低效的大数组,其中有许多未使用的条目,即使它编译了也是如此)。

无论如何,由于它是根据“零条款BSD许可证”(0BSD)发布的,这是一种宽松的许可证,更准确地说是公共领域等效许可证,您可以随心所欲地使用它。

以下是从我的库中直接复制粘贴的函数,以便这个答案可以独立存在。注意:

  1. 截至2019年8月初,这涵盖了我能找到的Linux、Darwin(Mac OS X和iOS X)、FreeBSD、NetBSD、OpenBSD、DragonflyBSD以及几个闭源Unix的所有errno名称。

  2. 如果您提供了一个errno值,但它不知道该名称,则返回NULL指针。

#include <errno.h>

char const * errnoname(int errno_)
{
    switch(errno_)
    {
#ifdef E2BIG
        case E2BIG: return "E2BIG";
#endif
#ifdef EACCES
        case EACCES: return "EACCES";
#endif
#ifdef EADDRINUSE
        case EADDRINUSE: return "EADDRINUSE";
#endif
#ifdef EADDRNOTAVAIL
        case EADDRNOTAVAIL: return "EADDRNOTAVAIL";
#endif
#ifdef EADI
        case EADI: return "EADI";
#endif
#ifdef EADV
        case EADV: return "EADV";
#endif
#ifdef EAFNOSUPPORT
        case EAFNOSUPPORT: return "EAFNOSUPPORT";
#endif
#ifdef EAGAIN
        case EAGAIN: return "EAGAIN";
#endif
#ifdef EAIO
        case EAIO: return "EAIO";
#endif
#ifdef EALIGN
        case EALIGN: return "EALIGN";
#endif
#ifdef EALREADY
        case EALREADY: return "EALREADY";
#endif
#ifdef EASYNC
        case EASYNC: return "EASYNC";
#endif
#ifdef EAUTH
        case EAUTH: return "EAUTH";
#endif
#ifdef EBADARCH
        case EBADARCH: return "EBADARCH";
#endif
#ifdef EBADE
        case EBADE: return "EBADE";
#endif
#ifdef EBADEXEC
        case EBADEXEC: return "EBADEXEC";
#endif
#ifdef EBADF
        case EBADF: return "EBADF";
#endif
#ifdef EBADFD
        case EBADFD: return "EBADFD";
#endif
#ifdef EBADMACHO
        case EBADMACHO: return "EBADMACHO";
#endif
#ifdef EBADMSG
        case EBADMSG: return "EBADMSG";
#endif
#ifdef EBADR
        case EBADR: return "EBADR";
#endif
#ifdef EBADRPC
        case EBADRPC: return "EBADRPC";
#endif
#ifdef EBADRQC
        case EBADRQC: return "EBADRQC";
#endif
#ifdef EBADSLT
        case EBADSLT: return "EBADSLT";
#endif
#ifdef EBADVER
        case EBADVER: return "EBADVER";
#endif
#ifdef EBFONT
        case EBFONT: return "EBFONT";
#endif
#ifdef EBUSY
        case EBUSY: return "EBUSY";
#endif
#ifdef ECANCELED
        case ECANCELED: return "ECANCELED";
#endif
#if defined(ECANCELLED) && (!defined(ECANCELED) || ECANCELLED != ECANCELED)
        case ECANCELLED: return "ECANCELLED";
#endif
#ifdef ECAPMODE
        case ECAPMODE: return "ECAPMODE";
#endif
#ifdef ECHILD
        case ECHILD: return "ECHILD";
#endif
#ifdef ECHRNG
        case ECHRNG: return "ECHRNG";
#endif
#ifdef ECKPT
        case ECKPT: return "ECKPT";
#endif
#ifdef ECLONEME
        case ECLONEME: return "ECLONEME";
#endif
#ifdef ECOMM
        case ECOMM: return "ECOMM";
#endif
#ifdef ECONFIG
        case ECONFIG: return "ECONFIG";
#endif
#ifdef ECONNABORTED
        case ECONNABORTED: return "ECONNABORTED";
#endif
#ifdef ECONNREFUSED
        case ECONNREFUSED: return "ECONNREFUSED";
#endif
#ifdef ECONNRESET
        case ECONNRESET: return "ECONNRESET";
#endif
#ifdef ECORRUPT
        case ECORRUPT: return "ECORRUPT";
#endif
#ifdef ECVCERORR
        case ECVCERORR: return "ECVCERORR";
#endif
#ifdef ECVPERORR
        case ECVPERORR: return "ECVPERORR";
#endif
#ifdef EDEADLK
        case EDEADLK: return "EDEADLK";
#endif
#if defined(EDEADLOCK) && (!defined(EDEADLK) || EDEADLOCK != EDEADLK)
        case EDEADLOCK: return "EDEADLOCK";
#endif
#ifdef EDESTADDREQ
        case EDESTADDREQ: return "EDESTADDREQ";
#endif
#ifdef EDESTADDRREQ
        case EDESTADDRREQ: return "EDESTADDRREQ";
#endif
#ifdef EDEVERR
        case EDEVERR: return "EDEVERR";
#endif
#ifdef EDIRIOCTL
        case EDIRIOCTL: return "EDIRIOCTL";
#endif
#ifdef EDIRTY
        case EDIRTY: return "EDIRTY";
#endif
#ifdef EDIST
        case EDIST: return "EDIST";
#endif
#ifdef EDOM
        case EDOM: return "EDOM";
#endif
#ifdef EDOOFUS
        case EDOOFUS: return "EDOOFUS";
#endif
#ifdef EDOTDOT
        case EDOTDOT: return "EDOTDOT";
#endif
#ifdef EDQUOT
        case EDQUOT: return "EDQUOT";
#endif
#ifdef EDUPFD
        case EDUPFD: return "EDUPFD";
#endif
#ifdef EDUPPKG
        case EDUPPKG: return "EDUPPKG";
#endif
#ifdef EEXIST
        case EEXIST: return "EEXIST";
#endif
#ifdef EFAIL
        case EFAIL: return "EFAIL";
#endif
#ifdef EFAULT
        case EFAULT: return "EFAULT";
#endif
#ifdef EFBIG
        case EFBIG: return "EFBIG";
#endif
#ifdef EFORMAT
        case EFORMAT: return "EFORMAT";
#endif
#ifdef EFSCORRUPTED
        case EFSCORRUPTED: return "EFSCORRUPTED";
#endif
#ifdef EFTYPE
        case EFTYPE: return "EFTYPE";
#endif
#ifdef EHOSTDOWN
        case EHOSTDOWN: return "EHOSTDOWN";
#endif
#ifdef EHOSTUNREACH
        case EHOSTUNREACH: return "EHOSTUNREACH";
#endif
#ifdef EHWPOISON
        case EHWPOISON: return "EHWPOISON";
#endif
#ifdef EIDRM
        case EIDRM: return "EIDRM";
#endif
#ifdef EILSEQ
        case EILSEQ: return "EILSEQ";
#endif
#ifdef EINIT
        case EINIT: return "EINIT";
#endif
#ifdef EINPROG
        case EINPROG: return "EINPROG";
#endif
#ifdef EINPROGRESS
        case EINPROGRESS: return "EINPROGRESS";
#endif
#ifdef EINTEGRITY
        case EINTEGRITY: return "EINTEGRITY";
#endif
#ifdef EINTR
        case EINTR: return "EINTR";
#endif
#ifdef EINVAL
        case EINVAL: return "EINVAL";
#endif
#ifdef EIO
        case EIO: return "EIO";
#endif
#ifdef EIPSEC
        case EIPSEC: return "EIPSEC";
#endif
#ifdef EISCONN
        case EISCONN: return "EISCONN";
#endif
#ifdef EISDIR
        case EISDIR: return "EISDIR";
#endif
#ifdef EISNAM
        case EISNAM: return "EISNAM";
#endif
#ifdef EJUSTRETURN
        case EJUSTRETURN: return "EJUSTRETURN";
#endif
#ifdef EKEEPLOOKING
        case EKEEPLOOKING: return "EKEEPLOOKING";
#endif
#ifdef EKEYEXPIRED
        case EKEYEXPIRED: return "EKEYEXPIRED";
#endif
#ifdef EKEYREJECTED
        case EKEYREJECTED: return "EKEYREJECTED";
#endif
#ifdef EKEYREVOKED
        case EKEYREVOKED: return "EKEYREVOKED";
#endif
#ifdef EL2HLT
        case EL2HLT: return "EL2HLT";
#endif
#ifdef EL2NSYNC
        case EL2NSYNC: return "EL2NSYNC";
#endif
#ifdef EL3HLT
        case EL3HLT: return "EL3HLT";
#endif
#ifdef EL3RST
        case EL3RST: return "EL3RST";
#endif
#ifdef ELIBACC
        case ELIBACC: return "ELIBACC";
#endif
#ifdef ELIBBAD
        case ELIBBAD: return "ELIBBAD";
#endif
#ifdef ELIBEXEC
        case ELIBEXEC: return "ELIBEXEC";
#endif
#ifdef ELIBMAX
        case ELIBMAX: return "ELIBMAX";
#endif
#ifdef ELIBSCN
        case ELIBSCN: return "ELIBSCN";
#endif
#ifdef ELNRNG
        case ELNRNG: return "ELNRNG";
#endif
#ifdef ELOCKUNMAPPED
        case ELOCKUNMAPPED: return "ELOCKUNMAPPED";
#endif
#ifdef ELOOP
        case ELOOP: return "ELOOP";
#endif
#ifdef EMEDIA
        case EMEDIA: return "EMEDIA";
#endif
#ifdef EMEDIUMTYPE
        case EMEDIUMTYPE: return "EMEDIUMTYPE";
#endif
#ifdef EMFILE
        case EMFILE: return "EMFILE";
#endif
#ifdef EMLINK
        case EMLINK: return "EMLINK";
#endif
#ifdef EMOUNTEXIT
        case EMOUNTEXIT: return "EMOUNTEXIT";
#endif
#ifdef EMOVEFD
        case EMOVEFD: return "EMOVEFD";
#endif
#ifdef EMSGSIZE
        case EMSGSIZE: return "EMSGSIZE";
#endif
#ifdef EMTIMERS
        case EMTIMERS: return "EMTIMERS";
#endif
#ifdef EMULTIHOP
        case EMULTIHOP: return "EMULTIHOP";
#endif
#ifdef ENAMETOOLONG
        case ENAMETOOLONG: return "ENAMETOOLONG";
#endif
#ifdef ENAVAIL
        case ENAVAIL: return "ENAVAIL";
#endif
#ifdef ENEEDAUTH
        case ENEEDAUTH: return "ENEEDAUTH";
#endif
#ifdef ENETDOWN
        case ENETDOWN: return "ENETDOWN";
#endif
#ifdef ENETRESET
        case ENETRESET: return "ENETRESET";
#endif
#ifdef ENETUNREACH
        case ENETUNREACH: return "ENETUNREACH";
#endif
#ifdef ENFILE
        case ENFILE: return "ENFILE";
#endif
#ifdef ENFSREMOTE
        case ENFSREMOTE: return "ENFSREMOTE";
#endif
#ifdef ENOANO
        case ENOANO: return "ENOANO";
#endif
#ifdef ENOATTR
        case ENOATTR: return "ENOATTR";
#endif
#ifdef ENOBUFS
        case ENOBUFS: return "ENOBUFS";
#endif
#ifdef ENOCONNECT
        case ENOCONNECT: return "ENOCONNECT";
#endif
#ifdef ENOCSI
        case ENOCSI: return "ENOCSI";
#endif
#ifdef ENODATA
        case ENODATA: return "ENODATA";
#endif
#ifdef ENODEV
        case ENODEV: return "ENODEV";
#endif
#ifdef ENOENT
        case ENOENT: return "ENOENT";
#endif
#ifdef ENOEXEC
        case ENOEXEC: return "ENOEXEC";
#endif
#ifdef ENOIOCTL
        case ENOIOCTL: return "ENOIOCTL";
#endif
#ifdef ENOKEY
        case ENOKEY: return "ENOKEY";
#endif
#ifdef ENOLCK
        case ENOLCK: return "ENOLCK";
#endif
#ifdef ENOLINK
        case ENOLINK: return "ENOLINK";
#endif
#ifdef ENOLOAD
        case ENOLOAD: return "ENOLOAD";
#endif
#ifdef ENOMATCH
        case ENOMATCH: return "ENOMATCH";
#endif
#ifdef ENOMEDIUM
        case ENOMEDIUM: return "ENOMEDIUM";
#endif
#ifdef ENOMEM
        case ENOMEM: return "ENOMEM";
#endif
#ifdef ENOMSG
        case ENOMSG: return "ENOMSG";
#endif
#ifdef ENONET
        case ENONET: return "ENONET";
#endif
#ifdef ENOPKG
        case ENOPKG: return "ENOPKG";
#endif
#ifdef ENOPOLICY
        case ENOPOLICY: return "ENOPOLICY";
#endif
#ifdef ENOPROTOOPT
        case ENOPROTOOPT: return "ENOPROTOOPT";
#endif
#ifdef ENOREG
        case ENOREG: return "ENOREG";
#endif
#ifdef ENOSPC
        case ENOSPC: return "ENOSPC";
#endif
#ifdef ENOSR
        case ENOSR: return "ENOSR";
#endif
#ifdef ENOSTR
        case ENOSTR: return "ENOSTR";
#endif
#ifdef ENOSYM
        case ENOSYM: return "ENOSYM";
#endif
#ifdef ENOSYS
        case ENOSYS: return "ENOSYS";
#endif
#ifdef ENOTACTIVE
        case ENOTACTIVE: return "ENOTACTIVE";
#endif
#ifdef ENOTBLK
        case ENOTBLK: return "ENOTBLK";
#endif
#ifdef ENOTCAPABLE
        case ENOTCAPABLE: return "ENOTCAPABLE";
#endif
#ifdef ENOTCONN
        case ENOTCONN: return "ENOTCONN";
#endif
#ifdef ENOTDIR
        case ENOTDIR: return "ENOTDIR";
#endif
#ifdef ENOTEMPTY
        case ENOTEMPTY: return "ENOTEMPTY";
#endif
#ifdef ENOTNAM
        case ENOTNAM: return "ENOTNAM";
#endif
#ifdef ENOTREADY
        case ENOTREADY: return "ENOTREADY";
#endif
#ifdef ENOTRECOVERABLE
        case ENOTRECOVERABLE: return "ENOTRECOVERABLE";
#endif
#ifdef ENOTRUST
        case ENOTRUST: return "ENOTRUST";
#endif
#ifdef ENOTSOCK
        case ENOTSOCK: return "ENOTSOCK";
#endif
#ifdef ENOTSUP
        case ENOTSUP: return "ENOTSUP";
#endif
#ifdef ENOTTY
        case ENOTTY: return "ENOTTY";
#endif
#ifdef ENOTUNIQ
        case ENOTUNIQ: return "ENOTUNIQ";
#endif
#ifdef ENOUNLD
        case ENOUNLD: return "ENOUNLD";
#endif
#ifdef ENOUNREG
        case ENOUNREG: return "ENOUNREG";
#endif
#ifdef ENXIO
        case ENXIO: return "ENXIO";
#endif
#ifdef EOPCOMPLETE
        case EOPCOMPLETE: return "EOPCOMPLETE";
#endif
#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || EOPNOTSUPP != ENOTSUP)
        case EOPNOTSUPP: return "EOPNOTSUPP";
#endif
#ifdef EOVERFLOW
        case EOVERFLOW: return "EOVERFLOW";
#endif
#ifdef EOWNERDEAD
        case EOWNERDEAD: return "EOWNERDEAD";
#endif
#ifdef EPASSTHROUGH
        case EPASSTHROUGH: return "EPASSTHROUGH";
#endif
#ifdef EPATHREMOTE
        case EPATHREMOTE: return "EPATHREMOTE";
#endif
#ifdef EPERM
        case EPERM: return "EPERM";
#endif
#ifdef EPFNOSUPPORT
        case EPFNOSUPPORT: return "EPFNOSUPPORT";
#endif
#ifdef EPIPE
        case EPIPE: return "EPIPE";
#endif
#ifdef EPOWERF
        case EPOWERF: return "EPOWERF";
#endif
#ifdef EPROCLIM
        case EPROCLIM: return "EPROCLIM";
#endif
#ifdef EPROCUNAVAIL
        case EPROCUNAVAIL: return "EPROCUNAVAIL";
#endif
#ifdef EPROGMISMATCH
        case EPROGMISMATCH: return "EPROGMISMATCH";
#endif
#ifdef EPROGUNAVAIL
        case EPROGUNAVAIL: return "EPROGUNAVAIL";
#endif
#ifdef EPROTO
        case EPROTO: return "EPROTO";
#endif
#ifdef EPROTONOSUPPORT
        case EPROTONOSUPPORT: return "EPROTONOSUPPORT";
#endif
#ifdef EPROTOTYPE
        case EPROTOTYPE: return "EPROTOTYPE";
#endif
#ifdef EPWROFF
        case EPWROFF: return "EPWROFF";
#endif
#ifdef EQFULL
        case EQFULL: return "EQFULL";
#endif
#ifdef EQSUSPENDED
        case EQSUSPENDED: return "EQSUSPENDED";
#endif
#ifdef ERANGE
        case ERANGE: return "ERANGE";
#endif
#ifdef ERECYCLE
        case ERECYCLE: return "ERECYCLE";
#endif
#ifdef EREDRIVEOPEN
        case EREDRIVEOPEN: return "EREDRIVEOPEN";
#endif
#ifdef EREFUSED
        case EREFUSED: return "EREFUSED";
#endif
#ifdef ERELOC
        case ERELOC: return "ERELOC";
#endif
#ifdef ERELOCATED
        case ERELOCATED: return "ERELOCATED";
#endif
#ifdef ERELOOKUP
        case ERELOOKUP: return "ERELOOKUP";
#endif
#ifdef EREMCHG
        case EREMCHG: return "EREMCHG";
#endif
#ifdef EREMDEV
        case EREMDEV: return "EREMDEV";
#endif
#ifdef EREMOTE
        case EREMOTE: return "EREMOTE";
#endif
#ifdef EREMOTEIO
        case EREMOTEIO: return "EREMOTEIO";
#endif
#ifdef EREMOTERELEASE
        case EREMOTERELEASE: return "EREMOTERELEASE";
#endif
#ifdef ERESTART
        case ERESTART: return "ERESTART";
#endif
#ifdef ERFKILL
        case ERFKILL: return "ERFKILL";
#endif
#ifdef EROFS
        case EROFS: return "EROFS";
#endif
#ifdef ERPCMISMATCH
        case ERPCMISMATCH: return "ERPCMISMATCH";
#endif
#ifdef ESAD
        case ESAD: return "ESAD";
#endif
#ifdef ESHLIBVERS
        case ESHLIBVERS: return "ESHLIBVERS";
#endif
#ifdef ESHUTDOWN
        case ESHUTDOWN: return "ESHUTDOWN";
#endif
#ifdef ESOCKTNOSUPPORT
        case ESOCKTNOSUPPORT: return "ESOCKTNOSUPPORT";
#endif
#ifdef ESOFT
        case ESOFT: return "ESOFT";
#endif
#ifdef ESPIPE
        case ESPIPE: return "ESPIPE";
#endif
#ifdef ESRCH
        case ESRCH: return "ESRCH";
#endif
#ifdef ESRMNT
        case ESRMNT: return "ESRMNT";
#endif
#ifdef ESTALE
        case ESTALE: return "ESTALE";
#endif
#ifdef ESTART
        case ESTART: return "ESTART";
#endif
#ifdef ESTRPIPE
        case ESTRPIPE: return "ESTRPIPE";
#endif
#ifdef ESYSERROR
        case ESYSERROR: return "ESYSERROR";
#endif
#ifdef ETIME
        case ETIME: return "ETIME";
#endif
#ifdef ETIMEDOUT
        case ETIMEDOUT: return "ETIMEDOUT";
#endif
#ifdef ETOOMANYREFS
        case ETOOMANYREFS: return "ETOOMANYREFS";
#endif
#ifdef ETXTBSY
        case ETXTBSY: return "ETXTBSY";
#endif
#ifdef EUCLEAN
        case EUCLEAN: return "EUCLEAN";
#endif
#ifdef EUNATCH
        case EUNATCH: return "EUNATCH";
#endif
#ifdef EUSERS
        case EUSERS: return "EUSERS";
#endif
#ifdef EVERSION
        case EVERSION: return "EVERSION";
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || EWOULDBLOCK != EAGAIN)
        case EWOULDBLOCK: return "EWOULDBLOCK";
#endif
#ifdef EWRONGFS
        case EWRONGFS: return "EWRONGFS";
#endif
#ifdef EWRPROTECT
        case EWRPROTECT: return "EWRPROTECT";
#endif
#ifdef EXDEV
        case EXDEV: return "EXDEV";
#endif
#ifdef EXFULL
        case EXFULL: return "EXFULL";
#endif
    }
    return 0;
}

1
这是一个似乎可以做到的Perl技巧:

#!/usr/bin/perl -w
use strict;
no strict "refs";
use POSIX qw(:errno_h);

my $code = shift;

my $ns = \%{'POSIX::'};
foreach (keys %$ns) {
    print "$_\n" if /^E(?!XPORT)/ && (${$ns->{$_}}||-1) == $code;
}

我该怎么使用这个?它给了我一个错误信息:在./e.pl的第10行,不是GLOB引用。(Ubuntu 14.04,perl v5.18.2) - abligh
显然,这里有些东西与我使用的旧版Perl不同,哎呀。 - aschepler
更新至 Perl 5.20 版本。 - aschepler
我更新了你的答案,所以当 ${$ns->{$_}}undef 时,它不再打印未初始化的警告。现在它运行得很好! - abligh
还要注意的是,这是迄今为止唯一一个不需要解析包含文件(可能不可靠)或构建自己的表格的答案。 - abligh

1

以下是我的答案,结合了其他人的答案和一些perl代码:

#!/usr/bin/perl
use strict;
use warnings;
die "Syntax: error ERRORNUM" unless ($#ARGV==0);
open (my $p, "cpp -dM /usr/include/errno.h |") || die ("Cannot preprocess headers: $!");
while (<$p>)
{
    chomp;
    print "$1\n" if /^#define (E\w+) (\d+)/ && $2==$ARGV[0];
}
close ($p);

使用示例:

$ ./error 10
ECHILD

我相信加入一些错误处理会使其更好。


0

您可以使用符号本身编写错误符号表。显然,这要求您包括定义您想考虑的常量的所有标头。

常量列表是从 errno手册页面 复制并粘贴的。

如果在下面的实现中未知符号,则编写通用宏,例如 ERROR_161。如果您知道生成代码的系统调用,请查阅其文档并手动包含可能的错误代码。这样,您将随着时间的推移建立一个代码库。(我同意这不是非常优雅。)

这些代码由宏创建,并存储为具有指定初始值设定项的静态字符串数组。数组大小由最大索引确定。

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

// ... more sys includes as appropriate ...

#define ERRNAME(X) [X] = #X

static const char *errname[] = {
    ERRNAME(E2BIG),
    ERRNAME(EACCES),
    ERRNAME(EADDRINUSE),
    ERRNAME(EADDRNOTAVAIL),
    ERRNAME(EAFNOSUPPORT),
    ERRNAME(EAGAIN),
    // ... more symbols ...
};

const char *errsym(int e)
{
    static char buf[20];

    if (e >= 0 && e < (sizeof(errname) / sizeof(*errname))) {
        if (errname[e]) return errname[e];
    }

    snprintf(buf, sizeof(buf), "ERROR_%u", (unsigned int) e);
    return buf;
}

你应该知道将其变成一个数组存在风险:1)无法保证errno值连续或者相对较小,2)某些系统上,一些errno名称可能具有相同的编号。 - mtraceur

0
至少在Ubuntu上,有一个叫做moreutils的软件包,其中包含一个名为errno的程序,由Lars Wirzenius编写,它可以实现你想要的功能:
% errno 10 20 30 666
ECHILD 10 No child processes
ENOTDIR 20 Not a directory
EROFS 30 Read-only file system

我已经测试了这个程序并与Python程序进行了比较,它至少映射了Python中所有可用的错误。然而,我的Python只有130个代码;errno产生了134个代码。其中有3个是重复的,因此反向映射不是唯一的。在Python的errno模块中缺失的一个,在errno中是可用的,即EHWPOISON 133 Memory page has hardware error


0
通常(总是?),strerror()函数在返回的消息中会提供该常量的名称。这只是一个技巧,但你可以使用它来找到以'E'开头的第一个连续大写字母组合...

1
实际测试表明,我的“通常/总是”非常乐观。 =/ - BadZen
它们大多是标准的,因此可能采用带有许多case EPERM: { return "EPERM"; }的巨大开关实现是正确的方法。 - BadZen
我从未见过它这样做。在我提到的情况下它不会这样做(请参见问题)。 - abligh
是的,看起来它不适用于它们中的任何一个 - 这让我想起了另一个函数 strerror()... - BadZen

0

编辑: 在问题中的错别字被修正之后,与此无关的部分已经移除。

根据标题所述的 POSIX,你应该已经完成了在你的问题中提到的 errno.h 头文件。

查看标准以查看应该在其中定义的完整宏列表。(http://pubs.opengroup.org/onlinepubs/9699919799/

使用 gcc,你应该能够在包含 errno.h 后使用 gcc -dM -E /usr/include/errno.h 获取所有宏定义列表,而不必手动寻找并开始遍历整个包含树。

使用启发式方法,几乎所有以大写字母 E 开头的宏定义都应该是错误代码,你可以使用 grep、awk 或你选择的工具进一步缩小列表。

gcc -dM -E /usr/include/errno.h | awk '$2 ~ /^E/ {print $3 "\t" $2}' 是我在撰写本行时正在尝试的版本。

与此同时,在评论区中,abligh 提出了以下代码:gcc -dM -E /usr/include/errno.h | egrep "^#define E" | cut -c 9- | sort -n -k 2,这应该会提供一个排序后的列表。


不是我给你点了踩,那只是一个打字错误。在 errno.h 中也找不到它。错误定义(至少在Linux中)在一系列嵌套的包含文件中,因此不容易找到。 - abligh
此外,POSIX标准在这里的作用很少,因为错误的可能会因实现而异(例如,在Linux和OS-X之间)。这正是我想回到符号常量的原因! - abligh
gcc -dM -E /usr/include/errno.h 应该会给你返回 errno.h所有宏的定义,因为 gcc 会为你做头文件包含路径的查找。我会将这个建议添加到答案中。 - mikyra
有趣。我能做的最好的就是 gcc -dM -E /usr/include/errno.h | egrep "^#define E" | cut -c 9- | sort -n -k 2,它似乎主要按顺序列出了它们。请随意将其包含在您的答案中(或者以不那么hacky的方式编写),我会点赞的。 - abligh

0
Perl已经过时了,现在可以用Python来完成,而且Python更容易获取! 它还有一个名为errno的模块,里面有一个称为 errno.errorcode 的反向映射。
>>> import errno
>>> errno.errorcode[10]
'ECHILD'

从命令行:

python -c 'import errno; print(errno.errorcode[10])'

或者将其放入独立的脚本中:

#!/usr/bin/python
import errno, sys

for i in sys.argv[1:]:
    code = int(i)
    print('{0:8d} - {1}'.format(code, errno.errorcode.get(code, '<unknown>')))

然后 errno.py 10 20 30 666 将会打印出来

      10 - ECHILD
      20 - ENOTDIR
      30 - EROFS
     666 - <unknown>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接