UNIX 中是否有任何函数可以将 errno 转换为相应的字符串,例如 EIDRM 到“EIDRM”。调试这些整数错误来检查错误非常烦人。
6 回答
strerror()
应该这样做。 http://linux.die.net/man/3/strerror
仅供参考,这样您自己就可以更轻松地找到这些东西:如果您输入 man errno (或您正在研究的任何功能),并查看手册页的最底部,您将看到相关功能的列表。如果你们man
每个人(根据他们的名字猜测首先要做什么),您通常会找到类似问题的答案。
只是另一种解决方案,可以完全解决您遇到的问题,但使用 Python 而不是 C:
>>> import errno
>>> errno.errorcode[errno.EIDRM]
'EIDRM'
现在有一个errno
与moreutils包一起分发的实用程序。
$ errno -h
Usage: errno [-lsS] [--list] [--search] [--search-all-locales] [keyword]
$ errno -l
EPERM 1 Operation not permitted
ENOENT 2 No such file or directory
ESRCH 3 No such process
EINTR 4 Interrupted system call
EIO 5 Input/output error
ENXIO 6 No such device or address
E2BIG 7 Argument list too long
ENOEXEC 8 Exec format error
...
$ errno 11
EAGAIN 11 Resource temporarily unavailable
$ errno -s Ouput
EIO 5 Input/output error
我不确定这种enum
风格的名称,但出于调试和错误报告的目的,您可以使用返回错误代码的人类可读表示的 C 函数perror(3)
。strerror(3)
有关详细信息,请参阅手册页。
如果您确实想要 EIDRM 而不是它的错误字符串:否。然而,在 OpenBSD 上,
man errno|egrep ' [0-9]+ E[A-Z]+'|sed 's/^ *//'|cut -d' ' -f1,2
打印出一张漂亮的“...\n89 EIDM\n...”表格,您可以将其进一步转换为您希望使用此功能的编程语言的数据结构。
在 UNIX 中没有执行此操作的标准函数。
但我最近编写了一个errnoname
库,它有一个errnoname
功能正是这样做的。
strerror
(也strerror_r
, strerror_l
, perror
)打印关于错误是什么的“人类友好”提示,但它们的输出是
- 通常无证,
- 不规范,
- 不保证遵循长度或格式的任何限制,
- 通常根据区域设置而有所不同,并且
- 并不总是在所有情况下都有意义(例如,它们倾向于打印
File exists
forEEXIST
,即使该错误通常针对非文件返回)。
这意味着它们表面上是用户友好的,但
- 其他代码无法可靠地解析它们(对于自动化、监控、脚本、包装程序等来说更糟),
- 在极端情况下可能会误导,并且
- 妨碍技术经验丰富的用户。
具有讽刺意味的是,没有什么能阻止这些函数在所有情况下仅使用errno
符号名称作为它们的错误字符串——它会在标准的字母范围内,特别是如果它们只针对特定的语言环境,比如特殊的C
语言环境。但是我所知道的没有 libc 可以做到这一点。
无论如何,由于 myerrnoname
是在“零条款 BSD 许可证”(0BSD)下发布的,这是一个许可许可证,或者更准确地说是一个等效于公共领域的许可证,你可以用它做任何你想做的事情。
为了使这个答案独立,同时仍然符合答案字符限制,下面是该errnoname
函数的两个缩写变体。
它们都被实现了,但在errnoname
这里我已经分离出每个的要点以使它们更具可读性。
几点注意事项:
errno
截至 2020 年 1 月,这涵盖了 Linux、Darwin(Mac OS X 和 iOS X)、FreeBSD、NetBSD、OpenBSD、DragonflyBSD 和几个闭源 Unix 的全部或大部分名称。如果你给它一个
errno
不知道名字的值,它会返回一个空指针。
变体 1:简单、通用
这个非常便携且简单,无需担心边缘情况。它可以使用几乎任何 C89 或更好的编译器进行编译,您可以使用它。(甚至可能是 C++ 编译器,随着语言的不同,这种编译器变得越来越少。)
当优化足够高时,此变体可以在现代编译器上编译为非常高效的代码(数组查找而不是 switch 语句),但可能不取决于具体情况。
#include <errno.h>
char const * errnoname(int errno_)
{
switch(errno_)
{
#ifdef E2BIG
case E2BIG: return "E2BIG";
#endif
#ifdef EACCES
case EACCES: return "EACCES";
#endif
/*
repeat for the other 100+ errno names,
don't forget to handle possible duplicates
like EAGAIN and EWOULDBLOCK
*/
}
return 0;
}
变体 2:显式高效,适用于大多数系统
这显然更有效,并且将非常可靠地编译为高效代码,因为它使数组查找显式并且不依赖于计算机优化。
只要系统具有正的、相对较小且合理连续的errno
值,它就可以安全使用。
只能在为数组实现乱序指定初始值设定项的编译器上编译(C99 或更高版本,目前不包括所有 C++ 版本。)
#include <errno.h>
char const * errnoname(int errno_)
{
static char const * const names[] =
{
#ifdef E2BIG
[E2BIG] = "E2BIG",
#endif
#ifdef EACCES
[EACCES] = "EACCES",
#endif
/*
repeat for the other 100+ errno names,
don't forget to handle possible duplicates
like EAGAIN and EWOULDBLOCK
*/
};
if(errno_ >= 0 && errno_ < (sizeof(names) / sizeof(*names)))
{
return names[errno_];
}
return 0;
}