【发布时间】:2013-11-22 00:22:24
【问题描述】:
我可以使用perror() 或strerror() 打印属于errno 的“人类可读”错误消息,但是如果我也想要打印符号名称(例如, errno 的“EAGAIN”)?
有什么方便的函数或宏来做到这一点?
更新:根据下面接受的答案及其 cmets 的想法,附上我最终编写的代码:
#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;
}
【问题讨论】:
-
有趣的代码。我不会在生产环境中部署一个运行 C 编译器的函数——它既慢又“不可靠”,因为有些人在运行时没有编译器。假设错误消息集通常不会更改,除非(可能)在操作系统升级时发生变化,因此有足够的机会缓存结果,即使这样,向后兼容性也意味着大多数错误编号保持不变。不过,您可以使用您的代码收集所有系统错误的数据,然后在编译时从数据驱动的函数中使用该输出。
-
@JonathanLeffler 感谢您的反馈。同意两个帐户。我可能应该提到,以上内容绝对不能这样部署(除非您不介意运行时对 gcc 和 gawk 的依赖......)。是的;像这样的代码最好在编译时作为 (c)make 脚本的一部分运行。不过,我的项目往往很小,所以我通常不会理会这样的脚本;这就是为什么上面的代码最适合我的个人调试版本。