如何将 UNIX 中的 errno 转换为相应的字符串?
Posted
技术标签:
【中文标题】如何将 UNIX 中的 errno 转换为相应的字符串?【英文标题】:How to convert errno in UNIX to corresponding string? 【发布时间】:2010-12-05 12:51:46 【问题描述】:在 UNIX 中是否有任何函数可以将 errno 转换为相应的字符串,例如EIDRM 到“EIDRM”。调试这些整数错误来检查错误非常烦人。
【问题讨论】:
【参考方案1】:strerror()
应该这样做。 http://linux.die.net/man/3/strerror
仅供参考,以便您自己更轻松地找到这些内容:如果您输入 man errno (或您正在调查的任何函数),然后查看手册页的最底部,您会看到一个列表相关功能。如果你man
每个人(根据他们的名字猜测首先要做什么),你通常会找到类似问题的答案。
【讨论】:
strerror 实际上会将它们变成比操作要求的“EIDRM”更有用的字符串,但这也是我应该给出的答案。 这些字符串实际上并没有那么有用,因为系统调用的手册页通常根据枚举代码指定错误行为。该字符串充其量只是一个提示。 危险,威尔罗宾逊!strerror()
的字符串是本地化的,当我检查“没有这样的文件或目录”的错误字符串时发现,但我的用户平台返回的是“Arquivo ou diretório não encontrado”。 (这是一种不暴露 errno 值本身的语言。)
@orm 是的,这些字符串在常见情况下对初学者非常有用,但在某些情况下,这些提示可能没有帮助甚至误导。
@davidgiven 听起来你遇到了导致我写the errnoname
library的那种用途:必须以编程方式解析错误字符串,因为这是提供错误信息的唯一形式对我来说,最大的罪犯是rm
、mv
和 iproute2 命令之类的工具,所有这些都缺乏其他方法来区分例如实际上意味着幂等成功的错误和其他错误。现在我们只需要说服每个人将errno
名称以可解析的方式放入他们的错误消息中。【参考方案2】:
只是另一种解决方案,可以完全解决您遇到的问题,但使用 Python 而不是 C:
>>> import errno
>>> errno.errorcode[errno.EIDRM]
'EIDRM'
【讨论】:
这是一个很好的答案,但值得知道的是,Python 仅包含所有可能的errno
值的子集,因此在某些系统上,它不会包含所有这些值的条目。对于典型情况,这不是问题,但如果您将其用作查找错误代码的通用方法,您可能会遇到这种情况。【参考方案3】:
现在有一个 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
【讨论】:
【参考方案4】:我不确定这种enum
样式的名称,但出于调试和错误报告的目的,您可以使用perror(3)
或strerror(3)
C 函数返回错误代码的人类可读表示。有关详细信息,请参阅手册页。
【讨论】:
我认为 perror() 只是打印到 STDERR 而不是将数字转换为值。我是否记错了(并误读了手册页)? 是的,perror
是针对errno
相关问题的调试工具,它会将消息写入STDERR
。【参考方案5】:
如果您确实想要 EIDRM 而不是它的错误字符串:否。但是,在 OpenBSD 上,
man errno|egrep ' [0-9]+ E[A-Z]+'|sed 's/^ *//'|cut -d' ' -f1,2
打印出一张漂亮的“...\n89 EIDM\n...”表格,您可以将其进一步转换为您希望使用此功能的编程语言的数据结构。
【讨论】:
【参考方案6】:在 UNIX 中没有执行此操作的标准函数。
但我最近写了 the errnoname
library ,它有一个 errnoname
函数可以做到这一点。
strerror
(also strerror_r
, strerror_l
, perror
) 打印关于错误是什么的“人性化”提示,但它们的输出是
File exists
打印为 EEXIST
,即使对于非文件的内容通常会返回该错误)。李>
这意味着它们表面上对用户友好,但
其他代码无法可靠地解析它们(对于自动化、监控、脚本、包装程序等来说更糟), 可能会在极端情况下产生误导,并且 妨碍有技术经验的用户。具有讽刺意味的是,没有任何东西阻止这些函数在所有情况下都使用errno
符号名称作为它们的错误字符串 - 它会在标准的字母范围内,特别是如果他们只是这样做的话对于特定的语言环境,例如特殊的 C
语言环境。但是我所知道的没有一个 libc 可以做到这一点。
无论如何,由于我的errnoname
是在“零条款 BSD 许可证”(0BSD)下发布的,这是一个许可许可证,或者更准确地说是公共领域等效许可证,你可以用它做任何你想做的事.
为了使这个答案独立,同时仍然符合答案字符限制,下面是errnoname
函数的两个缩写变体。
在errnoname
中,它们都已实现,但在这里我将它们的要点分开以使它们更具可读性。
几个笔记:
截至 2020 年 1 月,这涵盖了 Linux、Darwin(Mac OS X 和 ios X)、FreeBSD、NetBSD、OpenBSD、DragonflyBSD 和几个闭源 Unix 的全部或大部分 errno
名称。
如果你给它一个不知道名字的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;
【讨论】:
有一个 errnoname() 函数的好主意,但太糟糕了,它使用的是 switch-case。对于错误处理它可能很好,但对于其他目的,如跟踪器,它不必要地次优。您应该已经创建了一个大数组,使用 errno 代码作为索引。我的意思是,使用语法 ..., [index] = value, ... 。当然你也可以在那里打洞。 @vvaltchev 我现在添加了一个基于数组的实现作为选项(通过在编译时定义ERRNONAME_SAFE_TO_USE_ARRAY
激活)到errnoname。 (记录在Optimization 部分。)感谢您提出。在重新审视我想如何解决它之前,我一直在等待是否有对基于数组的方法的需求。
干得好,伙计!您的项目获得了它的第一个 GitHub 星 :-) 我为我的案例实现了一个自定义解决方案(我只需要 Linux errno 代码),但很高兴知道您添加了该功能。无论如何,是的,我理解理论上在某些系统上可能存在大 errno 代码的担忧,但实际上在我一生中见过的所有 UNIX 系统上,它们几乎只是序列号,通常总共不到几百个。 @mtraceur
@vvaltchev 进一步测试:我有一个 gcc 4.8.5(五年前发布),它设法将开关版本优化为自动使用-O2
、@ 的数组查找987654344@ 或 -Os
优化标志。 (这是在 ARMv7 Linux 设备上,其中errno
是严格连续的。然后很有趣的是,它无法将一对sub r0, r0, #1
和cmp r0, #132
指令组合成一个cmp r0, #133
指令,但除了基于开关和基于数组的 C 代码生成相同的机器代码。)
我认为一个指令的不同是因为它决定使数组有 132 个元素,其中第 0 个索引是第一个 errno,而不是第一个索引是第一个 errno 的手动数组版本。值得注意的是,手动添加 case 0: return 0
并没有改变结果 - 所以我猜优化器正在决定抽象形式的逻辑,它已经认识到零情况与任何其他越界相同这种情况下,这使它无法启发式地注意到一个额外的数组条目可能比一个始终执行的指令更有效。以上是关于如何将 UNIX 中的 errno 转换为相应的字符串?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Java 将 unix 纪元的列转换为 Apache spark DataFrame 中的日期?
如何将格式为 2018-12-27T02:23:29 的字符串转换为 C# 中的 Unix 时间戳