Python subprocess get children's output to file and terminal?
The call()
function is just Popen(*args, **kwargs).wait()
. You could call Popen
directly and use stdout=PIPE
argument to read from p.stdout
:
#!/usr/bin/env pythonimport sysfrom subprocess import Popen, PIPEfrom threading import Threaddef tee(infile, *files): """Print `infile` to `files` in a separate thread.""" def fanout(infile, *files): with infile: for line in iter(infile.readline, b""): for f in files: f.write(line) t = Thread(target=fanout, args=(infile,) + files) t.daemon = True t.start() return tdef teed_call(cmd_args, **kwargs): stdout, stderr = [kwargs.pop(s, None) for s in ["stdout", "stderr"]] p = Popen( cmd_args, stdout=PIPE if stdout is not None else None, stderr=PIPE if stderr is not None else None, **kwargs ) threads = [] if stdout is not None: threads.append( tee(p.stdout, stdout, getattr(sys.stdout, "buffer", sys.stdout)) ) if stderr is not None: threads.append( tee(p.stderr, stderr, getattr(sys.stderr, "buffer", sys.stderr)) ) for t in threads: t.join() # wait for IO completion return p.wait()outf, errf = open("out.txt", "wb"), open("err.txt", "wb")assert not teed_call(["cat", __file__], stdout=None, stderr=errf)assert not teed_call(["echo", "abc"], stdout=outf, stderr=errf, bufsize=0)assert teed_call(["gcc", "a b"], close_fds=True, stdout=outf, stderr=errf)
You could use something like this:https://github.com/waszil/subpiper
In your callbacks you can do whatever you like, log, write to file, print, etc. It also supports non-blocking mode.
from subpiper import subpiperdef my_stdout_callback(line: str): print(f'STDOUT: {line}')def my_stderr_callback(line: str): print(f'STDERR: {line}')my_additional_path_list = [r'c:\important_location']retcode = subpiper(cmd='echo magic', stdout_callback=my_stdout_callback, stderr_callback=my_stderr_callback, add_path_list=my_additional_path_list)