【问题标题】:Read from stdin in Python Process?从 Python 进程中的标准输入读取?
【发布时间】:2015-08-14 10:33:42
【问题描述】:

我正在尝试从 Python Process 对象内部读取 sys.stdin,但我不断收到“ValueError: I/O operation on closed file”结果。这是一个简单的例子:

import sys
from multiprocessing import Process

def do_something(input_data):
    for x in input_data:
        print x


input=sys.stdin

p = Process(target=do_something, args=(input,))
p.start() 
p.join() #Wait for Process to complete 

上面的脚本总是失败:

Traceback (most recent call last):
  File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap
    self.run()
  File "/usr/local/Cellar/python/2.7.5/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/process.py", line 114, in run
    self._target(*self._args, **self._kwargs)
  File "example.py", line 6, in do_something
    for x in input_data:
ValueError: I/O operation on closed file

当然,只需调用do_something(input) 就可以在不使用进程的情况下正常工作。创建一个Pipe() 对象似乎有帮助——我可以将标准输入的内容写入管道并从进程中以字符串形式获取结果——但我实际上需要以类似文件的形式输入一些下游操作。我可以将内容转储到一个文件中,然后从进程中重新读取它,但这看起来很笨拙,尤其是在标准输入非常大的情况下。是否有一些简单的方法可以从进程中读取 sys.stdin?

【问题讨论】:

    标签: python process stdin


    【解决方案1】:

    这是因为在进程启动之前,stdin 已关闭。否则可能会发生父进程和子进程(或多个子进程)都尝试从同一个标准输入读取,这是一个坏主意。

    在子进程中sys.stdin实际上被重定向到/dev/null

    from multiprocessing import Process
    import sys
    
    def test(*args):
        print(args)
        print(sys.stdin, sys.stdin.fileno())
    
    if __name__ == '__main__':
        p = Process(target=test, args=(sys.stdin,))
        p.start()
        p.join()
    

    应该打印类似这样的内容:

    (<closed file '<stdin>', mode 'r' at 0x7f3b4564b0c0>,)
    (<open file '/dev/null', mode 'r' at 0x7f3b43a9e0c0>, 3)
    

    此处传递的参数是对已关闭文件对象的引用,尝试使用它会引发您所看到的错误。

    您可以通过在父级中的sys.stdin.fileno() 上使用os.dup() 来解决此问题,并将返回的文件描述符副本作为参数传递给子级,然后您可以在其中使用os.fdopen() 来处理它。

    更简洁的解决方案可能是使用multiprocessing.Queue 读取父进程和pass it to the child 中的输入。

    【讨论】:

      【解决方案2】:

      您必须在某个时候关闭您尝试写入的文件。检查您的代码并尝试删除所有关闭文件的行 (fileVariableName.close()) 并查看它是否有效。如果确实如此,则将它们一一重新添加以查找问题。找到导致问题的行后,尝试将其进一步移动到程序中(稍后调用),看看是否能解决您的问题。

      编辑:改变

      def do_something(input_data):
          for x in input_data:
              print x
      

      def do_something():
          for x in sys.stdin:
              print x
      

      并摆脱input = sys.stdin

      【讨论】:

      • 我发布的示例代码演示了这个问题,但它不包括任何明确的文件关闭,所以我很确定不是它。还要记住“文件”是 sys.stdin,而不是常规文件。
      • 尝试将input=sys.stdin更改为input=sys.stdin.readlines()
      • 这有帮助,但它会在继续之前将所有标准输入读入内存,这是我希望避免的行为。输入可能非常大,我不想在开始流程之前阅读所有内容
      • 嗯,正在发生的事情是你正在输入等于sys.stdin 本身而不是那里的值(有点像文件)但是当你稍后在你的函数中调用它时它是由于某些原因未打开,为什么不尝试快速更改。我将编辑我的答案,因为这里没有足够的空间。