Python, выполнить шелл-код в отдельном процессе и получить ответ

3дpacтвуйте, вообщем идея такова: выполнить шелл-код в отдельном процессе и получить ответ. Я уверен что данный вопрос является дубликатом, но я не могу найти код.

Я пробовал реализовать это таким образом:

import subprocess, multiprocessing

def test(_command, _out_func):
    process= subprocess.Popen(_command, stdout=subprocess.PIPE, stdin= subprocess.PIPE, shell= True, close_fds=True)
    if _out_func: _out_func( _command, process.communicate()[0].decode() )

def run_command(command, out_func= None):
    process = multiprocessing.Process(target=test, args=([command, out_func]))
    process.start()

def o(command, output): print(command, ">", output, sep='')

run_command("python -c 'print(1)'", o)
#while 1: print(5)

Функция run_command принимает команду в качестве строки и функцию которая будет выполнена если нужно вернуть ответ от шелл-кода. Если функция вывода указана, то run_command будет ожидать ответа от шелл кода, а если функция не указана, то шелл код просто выполняется в отдельном процессе не ожидая вывода.

Проблема в том что если не указать функцию для вывода, то есть просто вызвать run_command("echo 1"), то выходит ошибка

sh: write error: Broken pipe

[Program finished]

Я понимаю что она связана с тем что основной процесс завершился, а subprocess.Popen пытается вернуть ответ в несуществующий процесс. Это и есть место на котором я застрял.

Как можно выполнить шелл-код и получить ответ, игнорируя BrokenPipeError: [Errno 32] Broken pipe?


Ответы (1 шт):

Автор решения: vadim vaduxa
import subprocess, threading

def test(*command):
    with subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) as proc:
        yield from proc.stdout

def run_command(*command, out_func=None):
    gen = test(*command)
    if out_func:
        return out_func(command, gen)
    else:
        def _out():
            for _ in gen: continue
            print(f'воход из потока {command}')
        threading.Thread(target=_out, daemon=False).start()

def out(command, output):
    print(command)
    for chunk in output:
        print(chunk.decode('cp866', 'ignore'))

run_command('python', '-c', 'print(1)', out_func=out)
run_command('echo', '1', out_func=out)
run_command('ping', '127.0.0.1')
print("выход из __main__, до завершения run_command('ping', '127.0.0.1')")

out:

('python', '-c', 'print(1)')
1

('echo', '1')
1

выход из __main__, до завершения run_command('ping', '127.0.0.1')
воход из потока ('ping', '127.0.0.1')
→ Ссылка