是否有一种跨平台的方式,可以使用ctypes可靠地找到stdout文件描述符?

6

我有一些使用ctypes的代码,试图确定由sys.stdout指向的文件是否实际上是stdout。我知道在任何符合POSIX标准的系统上,甚至在Windows上,如果sys.stdout.fileno() == 1,可以安全地假定这是正确的,因此我的问题不是如何在一般情况下执行此操作。

在我的代码中(已经在与我的问题无关的其他方面使用ctypes),我粗心地写了以下内容:

libc = ctypes.CDLL(ctypes.util.find_library('c'))
real_stdout = libc.fileno(ctypes.c_void_p.in_dll(libc, 'stdout'))
if sys.stdout.fileno() == real_stdout:
    ...

这在Linux上完全正常,所以我没有太多考虑。将1硬编码为文件描述符的可读性不如此代码。但是几天后我发现我的代码在OSX上无法工作。

原来OSX的libc没有导出任何名为“stdout”的符号。相反,它的stdio.h将stdout定义为:

#define stdout __stdoutp

如果我将我的代码更改为c_void_p.in_dll(libc,'__stdoutp'),我的代码就可以按预期工作,但当然这只适用于OSX。 结果,Windows也存在类似的问题(至少在使用MSVC时如此)。
我可能只会将我的代码更改为使用1,但出于好奇,我的问题仍然存在,是否有一种跨平台的方法可以获取stdio指针(以及类似地获取stdinstderr),而不需要假设它正在使用符合POSIX标准的描述符?

2
“文件描述符”不是跨平台的概念,而是特定于Posix的。您可以在任何Posix平台上使用fileno(stdout),尽管也有一个预定义的宏来表示该值,名为STDOUT_FILENO - Kerrek SB
请注意,如果 sys.stdout 被替换了,原始的 stdout 仍然存在于 sys.__stdout__ 中(除非您的代码也更改了它)。请查看 sys.__stdout__.fileno() => 1 - Dan D.
1
请注意,文件描述符(一个整数“句柄”)与C语言中的stdout FILE*是非常不同的东西。C库中的符号没有跨平台ABI,因此采用这种方法是徒劳的。将1硬编码为stdout的文件描述符几乎可以实现更好的可移植性。 - nos
sys.stdout.fileno() 也不能被依赖,因为它同样可以被替换。Nos 理解了问题,但正如我所怀疑的那样,实际上没有一个好的答案。 - Iguananaut
2个回答

4

通常情况下,如果您想要兼容性,就必须去查找相关标准,特别是在C语言中。由于您提到了Windows,我猜想您实际上需要的不是POSIX标准,而是C标准。

C99第7.19.1节定义了stdout为宏,因此不是变量。这意味着您无法依靠dlsym(我认为in_dll使用)来找到它。实际表达式可以是函数调用或固定地址,这也许不太可能,但确实可能存在...

如评论中所说,fileno函数反过来是由POSIX而非C定义的。C语言没有文件描述符的概念。我认为您最好假设POSIX并检查其指定的值1。


我会接受这个答案,因为大部分我所寻找的都是我已经知道的确认。虽然我不知道C标准将其定义为宏——这比任何事情都更加确认了这个问题没有真正的答案。而且你是对的——用dlsym来表达这个问题可能会更直接,因为ctypes在幕后使用它。 - Iguananaut

3

如果你只是对让事情工作感兴趣,而不是像我一样严格遵守标准,你可以通过编写一个简单的C代码段来找到stdout的“真实”名称:

echo -e '#include <stdio.h>\nFILE* mystdout = stdout;' > test.c
cpp test.c | tail

给你输出结果:
FILE* mystdout = __stdoutp;

这意味着您还需要尝试 ctypes.c_void_p.in_dll(libc, '__stdoutp') 来覆盖 darwin 的情况。

这是一个整洁明了的技巧;在将来寻找类似的东西时会很有帮助。 - Iguananaut

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