Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

catching stdout in realtime from subprocess

I want to subprocess.Popen() rsync.exe in Windows, and print the stdout in Python.

My code works, but it doesn't catch the progress until a file transfer is done! I want to print the progress for each file in real time.

Using Python 3.1 now since I heard it should be better at handling IO.

import subprocess, time, os, sys  cmd = "rsync.exe -vaz -P source/ dest/" p, line = True, 'start'   p = subprocess.Popen(cmd,                      shell=True,                      bufsize=64,                      stdin=subprocess.PIPE,                      stderr=subprocess.PIPE,                      stdout=subprocess.PIPE)  for line in p.stdout:     print(">>> " + str(line.rstrip()))     p.stdout.flush() 
like image 825
John A Avatar asked Oct 22 '09 12:10

John A


People also ask

How do I get stdout from subprocess run Python?

To capture the output of the subprocess. run method, use an additional argument named “capture_output=True”. You can individually access stdout and stderr values by using “output. stdout” and “output.

How do you get continuous output in Python?

Use While loop with True condition expression to take continuous input in Python. And break the loop using if statement and break statement.


1 Answers

Some rules of thumb for subprocess.

  • Never use shell=True. It needlessly invokes an extra shell process to call your program.
  • When calling processes, arguments are passed around as lists. sys.argv in python is a list, and so is argv in C. So you pass a list to Popen to call subprocesses, not a string.
  • Don't redirect stderr to a PIPE when you're not reading it.
  • Don't redirect stdin when you're not writing to it.

Example:

import subprocess, time, os, sys cmd = ["rsync.exe", "-vaz", "-P", "source/" ,"dest/"]  p = subprocess.Popen(cmd,                      stdout=subprocess.PIPE,                      stderr=subprocess.STDOUT)  for line in iter(p.stdout.readline, b''):     print(">>> " + line.rstrip()) 

That said, it is probable that rsync buffers its output when it detects that it is connected to a pipe instead of a terminal. This is the default behavior - when connected to a pipe, programs must explicitly flush stdout for realtime results, otherwise standard C library will buffer.

To test for that, try running this instead:

cmd = [sys.executable, 'test_out.py'] 

and create a test_out.py file with the contents:

import sys import time print ("Hello") sys.stdout.flush() time.sleep(10) print ("World") 

Executing that subprocess should give you "Hello" and wait 10 seconds before giving "World". If that happens with the python code above and not with rsync, that means rsync itself is buffering output, so you are out of luck.

A solution would be to connect direct to a pty, using something like pexpect.

like image 126
nosklo Avatar answered Sep 29 '22 21:09

nosklo