Есть ли способ передать 'stdin' в качестве аргумента другому процессу в python? - PullRequest
14 голосов
/ 23 января 2012

Я пытаюсь создать скрипт, который использует многопроцессорный модуль с python. Сценарий (назовем его 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()

это явно не работает, поскольку объекты sys.stdin различаются для основного процесса и p1. Поэтому я попытался это решить,

//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

Итак, я полагаю, что стандартный stdin-файл закрыт, и я не могу прочитать его. При таком соединении, как я могу передать основной файл 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 основных процессов в другом процессе .

заранее спасибо.

Ответы [ 4 ]

9 голосов
/ 24 января 2012

Самое простое - поменять местами 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()
2 голосов
/ 24 января 2012

Это будет только частичный ответ - поскольку я неясен в отношении последующих частей вопроса.

Вы начинаете с того, что ожидаете вызова своих сценариев:

$ 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 ... вы уже запускаете два процесса из командной строки - и вы используете оболочку длясоедините их с (анонимным) каналом (символ «|»), который соединяет стандартный вывод из первого сценария со стандартным вводом из второго сценария.

Цель объекта Process - управлять запуском второгопроцесс с первого раза.Вам нужно определить процесс;затем запустите его - тогда вы, вероятно, захотите подождать, пока он не завершится, прежде чем выйти из первого процесса ... (для этого достаточно вызвать p1.join () после p1.start ()).

Еслиесли вы хотите обмениваться данными между парой процессов, находящихся под управлением python, вы, вероятно, захотите использовать для этого объект multiprocess.Pipe .Затем вы можете легко обмениваться данными между начальным и подчиненным порожденным процессом, читая и записывая в / из объекта Pipe, а не стандартный ввод и стандартный вывод.Если вы действительно хотите перенаправить стандартный ввод и стандартный вывод, это, вероятно, возможно, если возиться с низкоуровневыми файловыми дескрипторами и / или переопределить / заменить объекты sys.stdin и sys.stdout ... но, я подозреваю,Вы, вероятно, не хотите (или не нуждаетесь) сделать это.

2 голосов
/ 23 января 2012

Каждый новый процесс, созданный с помощью многопроцессорного модуля, получает свой собственный 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()
1 голос
/ 24 января 2012

Чтобы прочитать введенный канал, используйте fileinput :

myscript.py

import fileinput

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