在Python中,有没有一种方法可以将'stdin'作为参数传递给另一个进程?

18
我尝试创建一个使用Python中的multiprocessing模块的脚本。该脚本(我们称其为myscript.py)将通过管道从另一个脚本中获取输入。
假设我像这样调用脚本;
$ python writer.py | python myscript.py 

这里是代码:

// writer.py
import time, sys

def main():
    while True:
        print "test"
        sys.stdout.flush()
        time.sleep(1)

main()

//myscript.py
def get_input():
    while True:
        text = sys.stdin.readline()
        print "hello " + text
        time.sleep(3)

if __name__ == '__main__':        
    p1 = Process(target=get_input, args=())
    p1.start()

显然这不起作用,因为主进程和p1进程的sys.stdin对象是不同的。所以我尝试了以下方法来解决这个问题:

//myscript.py
def get_input(temp):
    while True:
        text = temp.readline()
        print "hello " + text
        time.sleep(3)

if __name__ == '__main__':        
    p1 = Process(target=get_input, args=(sys.stdin,))
    p1.start()

但我遇到了这个错误;

Process Process-1:
Traceback (most recent call last):
  File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap
    self.run()
  File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run
    self._target(*self._args, **self._kwargs)
  File "in.py", line 12, in get_input
    text = temp.readline()
ValueError: I/O operation on closed file

所以,我猜测main的stdin文件已关闭,我无法从中读取。在这种情况下,如何将main的stdin文件传递给另一个进程?如果无法传递stdin,则如何从另一个进程中使用main的stdin?

更新: 好的,我需要澄清我的问题,因为人们认为使用多处理并不是必要的。 考虑像myscript.py这样的脚本;

//myscript.py
def get_input():
    while True:
        text = sys.stdin.readline()
        print "hello " + text
        time.sleep(3)

def do_more_things():
    while True:
        #// some code here
        time.sleep(60*5)

if __name__ == '__main__':        
    p1 = Process(target=get_input, args=())
    p1.start()

    do_more_things()

所以,我真的需要将 get_input() 函数与主函数(或其他子进程)并行运行。

抱歉有一些矛盾,我的英语还不错,但是我想我在这个问题上没有表述清楚。如果您能告诉我是否可以在另一个进程中使用主进程的 STDIN 对象,我会非常感激。

提前感谢。


1
你可以使用此处找到的语法https://dev59.com/NF_Va4cB1Zd3GeqPSVQr#8880555向stdin写入内容。在那里,它被用于读取,但你也可以使用相同的对象。 - Johan Lundberg
嗨,S.Lott,这只是我的问题的原型,myscript.py包含另一个进程,它有另一个无限循环并像守护进程一样运行,因此我不能在myscript主进程中读取它,因为writer.py也是无限的,并且需要作为守护进程运行。 @JohanLundberg,谢谢你的建议,我会检查它。 - Muhammet Can
1
这不就是 ( python writer.py | python myscript.py ) & python do_more_things.py 吗?我不明白为什么这不只是三个独立的 Python 程序。前两个构成一个微不足道的管道,第三个则完全无关。 - S.Lott
1
请问如何在Python中从标准输入读取数据? - monkut
可能是Python在子进程中使用STDIN的重复问题。 - n611x007
显示剩余5条评论
4个回答

12

最简单的方法是交换get_input()do_more_things(),即在父进程中读取sys.stdin

def get_input(stdin):
    for line in iter(stdin.readline, ''):
        print("hello", line, end='')
    stdin.close()

if __name__ == '__main__':
    p1 = mp.Process(target=do_more_things)
    p1.start()
    get_input(sys.stdin)

下一个最好的选择是使用 Thread() 而不是 Process() 来执行 get_input()
if __name__ == '__main__':
    t = Thread(target=get_input, args=(sys.stdin,))
    t.start()
    do_more_things()

如果以上方法无效,您可以尝试使用 os.dup()
newstdin = os.fdopen(os.dup(sys.stdin.fileno()))
try: 
   p = Process(target=get_input, args=(newstdin,))
   p.start()    
finally:
   newstdin.close() # close in the parent
do_more_things()

虽然我只需要第三个解决方案,但其他的解决方案也相当有效。感谢您花时间全面地帮助我。 - Muhammet Can

3
每次使用multiprocessing模块创建新进程都会获得一个独立的PID,因此它们拥有自己的标准输入设备和输出设备,即使它们都写入同一个终端,这就是需要锁的原因。
您已经通过将内容分成两个脚本并使用get_input()创建第三个进程来创建了两个进程。如果get_input()是线程而不是进程,则可以读取标准输入。然后,阅读器中无需睡眠功能。
## reader.py
from threading import Thread
import sys

def get_input():
    text = sys.stdin.readline()
    while len(text) != 0:
        print 'hello ' + text
        text = sys.stdin.readline()

if __name__ == '__main__':
    thread = Thread(target=get_input)
    thread.start()
    thread.join()

2
这只是一个部分回答 - 因为我不清楚问题的后续部分。
你开始说,你预计要调用你的脚本:
$ python writer.py | python myscript.py 

如果你要这样做,写入器需要将内容写到标准输出,而myscript需要从标准输入读取。第二个脚本应该是这样的:
def get_input():
    while True:
        text = sys.stdin.readline()
        print "hello " + text
        time.sleep(3)
if __name__ == '__main__':    
    get_input()

不需要使用multiprocessing.Process对象...你已经从命令行启动了两个进程,并且正在使用shell通过(匿名)管道("|"字符)将第一个脚本的标准输出连接到第二个脚本的标准输入。

Process对象的作用是从第一个进程管理启动第二个进程。您需要定义一个进程;然后启动它 - 然后您可能希望在退出第一个进程之前等待它终止...(在p1.start()之后调用p1.join()就足够了)。

如果您想在python控制下在一对进程之间进行通信,则可能需要使用multiprocess.Pipe对象来实现。然后,您可以通过读取和写入Pipe对象而不是标准输入和标准输出轻松地在初始进程和下属生成的进程之间进行通信。如果您真的想重新定向标准输入和标准输出,这可能可以通过操纵低级文件描述符和/或覆盖/替换sys.stdin和sys.stdout对象来实现...但我怀疑您可能不想(或不需要)这样做。


1

要读取管道输入,请使用fileinput

myscript.py

import fileinput

if __name__ == '__main__':
    for line in fileinput.input():
        #do stuff here
        process_line(line)

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