在Python中重定向通过F2PY调用的FORTRAN输出

14

我正在尝试弄清楚如何重定向从使用F2PY生成的Python接口运行的一些FORTRAN代码的输出。我尝试了:

from fortran_code import fortran_function
stdout_holder = sys.stdout
stderr_holder = sys.stderr
sys.stdout = file("/dev/null","w")
fortran_function()
sys.stdout.close()
sys.stderr.close()
sys.stdout = stdout_holder
sys.stderr = stderr_holder

这是Python中重定向输出的事实标准方法,但在此情况下似乎不起作用(即输出仍然显示)。

我找到了一篇来自2002年的邮件列表文章,其中说“可以从pts设备中读取消息,例如ttysnoop就是这样做的”。关于ttysnoop的信息在线上似乎很难找到(我认为它已经多年没有更新了;例如,“ttysnoop”的Google搜索结果中第一个链接只有指向tarballs、RPM和.deb的死链接),而这个要求在OS X上进行移植的请求得到的回复是“没戏,它需要一些我无法创建的Linux特定的utmp函数”。

我欢迎任何有关如何重定向输出的建议(不必使用ttysnoop)。

谢谢!


1
你确定Fortran的输出不是被发送到stderr而不是stdout吗? - Kamil Kisiel
是的,我也尝试过重定向,结果也一样。 - Samir Unni
2个回答

25

stdin和stdout文件描述符被C共享库继承。

from fortran_code import fortran_function
import os

print "will run fortran function!"

# open 2 fds
null_fds = [os.open(os.devnull, os.O_RDWR) for x in xrange(2)]
# save the current file descriptors to a tuple
save = os.dup(1), os.dup(2)
# put /dev/null fds on 1 and 2
os.dup2(null_fds[0], 1)
os.dup2(null_fds[1], 2)

# *** run the function ***
fortran_function()

# restore file descriptors so I can print the results
os.dup2(save[0], 1)
os.dup2(save[1], 2)
# close the temporary fds
os.close(null_fds[0])
os.close(null_fds[1])

print "done!"

这会同时抑制stderr输出吗?如果不是,那么如何实现呢? - Samir Unni
@aberration:不知道,你有没有测试过任何写入stderr的Fortran程序? - nosklo
我尝试了一下,它似乎能够显示写入stderr的文本。 - Samir Unni
3
感谢您提供这个非常有帮助的答案。当我将这段代码添加到一个需要在循环中多次重定向输出的脚本中时,我注意到我正在泄漏文件描述符。我认为您还需要在结尾处添加 os.close(save[0])os.close(save[1]) 以防止泄漏。 - josliber
运行得非常好,但FDS是什么的缩写? - Sibbs Gambling
2
@SibbsGambling 文件描述符 - nosklo

8

这是我最近写的一个上下文管理器,我觉得很有用,因为我在使用pymssql时遇到了类似于distutils.ccompiler.CCompiler.has_function的问题。我也使用了文件描述符的方法,但是我使用了上下文管理器。以下是我的代码:

import contextlib


@contextlib.contextmanager
def stdchannel_redirected(stdchannel, dest_filename):
    """
    A context manager to temporarily redirect stdout or stderr

    e.g.:


    with stdchannel_redirected(sys.stderr, os.devnull):
        if compiler.has_function('clock_gettime', libraries=['rt']):
            libraries.append('rt')
    """

    try:
        oldstdchannel = os.dup(stdchannel.fileno())
        dest_file = open(dest_filename, 'w')
        os.dup2(dest_file.fileno(), stdchannel.fileno())

        yield
    finally:
        if oldstdchannel is not None:
            os.dup2(oldstdchannel, stdchannel.fileno())
        if dest_file is not None:
            dest_file.close()

我创建这个的原因在于这篇博客文章中提到的背景。和你的情况相似。
我在setup.py中这样使用它:
with stdchannel_redirected(sys.stderr, os.devnull):
    if compiler.has_function('clock_gettime', libraries=['rt']):
        libraries.append('rt')

不错!可以灵活选择标准输出(stdout)、标准错误(stderr)和输出文件。 - undefined

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