我试图捕获并继续打印subprocess.run的stdout / stderr。基本上寻找内部T恤功能
目标(交互式python模式):
>>> import subprocess
>>> p = subprocess.run(['echo', 'random text'], stdout=(subprocess.PIPE, subprocess.STDOUT), stderr=(subprocess.PIPE, subprocess.STDERR))
random text
>>> out, err = p.communicate()
>>> print(out)
'random text'
>>> print(err)
''
上面的代码不起作用,但它显示了我想要完成的任务。关于如何确保stdout和stderr正常打印但是也被捕获的任何想法?
答案 0 :(得分:0)
我无法使用subprocess.run来捕获和打印输出,但我最终使用popen来完成此操作。 Popen不允许超时,因此我不得不向我们提供信号处理程序来促进这一点。
def tee(command: Sequence[str], *args, **kwargs):
import subprocess
import select
from signal import alarm, signal, SIGALRM, SIGKILL
stdout2 = ''
stderr2 = ''
# timeout configuration
class TimeoutAlarm(Exception):
pass
def timeout_alarm_handler(signum, frame):
raise TimeoutAlarm
# capture timeout if specified in kwargs
timeout = kwargs.pop('timeout') if kwargs.get('timeout') else None # type: # Optional[int]
if timeout:
assert isinstance(timeout, int), "Signal handler only support integers"
signal(SIGALRM, timeout_alarm_handler)
alarm(int(timeout))
# Configure stdout and stderr if specified
_ = kwargs.pop('stdout') if kwargs.get('stdout') else None
_ = kwargs.pop('stderr') if kwargs.get('stderr') else None
# remove universal_newlines as we always use universal_newlines
_ = kwargs.pop('universal_newlines') if kwargs.get('universal_newlines') is not None else None
# Execute a child program in a new process
with subprocess.Popen(command, *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, **kwargs) as process:
try: # TimeoutAlarm try block
stdout_fn = process.stdout.fileno()
stderr_fn = process.stderr.fileno()
# Continue reading stdout and stderr as long as the process has not terminated
while process.poll() is None:
# blocks until one file descriptor is ready
fds, _, _ = select.select([stdout_fn, stderr_fn], [], [])
# Capture the stdout
if stdout_fn in fds:
new_so = process.stdout.readline()
sys.stdout.write(new_so)
stdout2 += new_so
# Capture the stderr
if stderr_fn in fds:
new_se = process.stderr.readline()
sys.stderr.write(new_se)
stderr2 += new_se
return_code = process.returncode
except TimeoutAlarm:
os.kill(process.pid, SIGKILL)
return_code = process.returncode
raise subprocess.TimeoutExpired(cmd=command, timeout=timeout)
if return_code == 127:
raise OSError('Command not found')
elif return_code == 126:
raise OSError('Command invoked cannot execute')
return subprocess.CompletedProcess(args, return_code, stdout2, stderr2)
感谢Andrew Hoos的大部分内容:https://gist.github.com/AndrewHoos/9f03c74988469b517a7a