I'm trying to find a string in the output of proc.communicate for subprocess.Popen.
My code looks like this:
proc = subprocess.Popen(["./runCommand.sh" + " -i " + ip + " -c " + cmd], stdout=subprocess.PIPE, shell=True)
output = proc.communicate()
p_status = proc.wait()
if 'someword' in output:
#dosomething
It seems that I can't find the word I'm looking for in the output.
The output looks like this when printed:
(b'blabla someword\blabla\n', None)
Do I need to convert this in order to find something with "in"?
Edit:
Thanks so far for your answers!
I changed it to "output[0], but still I get an error
TypeError: a bytes-like object is required, not 'str'
What can I do here? Use decode()?
You are getting a two elements tuple, you can use in if you access to the first element of the tuple:
>>> 'someword' in (b'blabla someword\blabla\n', None)[0]
True
So you need to replace output with output[0] to make your code work.
You are getting both stdout + stderr into output, so you need to check if 'someword' in output[0]:
Or better yet:
proc = subprocess.Popen(["./runCommand.sh" + " -i " + ip + " -c " + cmd], stdout=subprocess.PIPE, shell=True)
output, _ = proc.communicate() # or output, err = proc.communicate()
p_status = proc.wait()
if 'someword' in output:
#dosomething
always be checking the doc:
In [7]: subprocess.Popen.communicate?
Signature: subprocess.Popen.communicate(self, input=None)
Docstring:
Interact with process: Send data to stdin. Read data from
stdout and stderr, until end-of-file is reached. Wait for
process to terminate. The optional input argument should be a
string to be sent to the child process, or None, if no data
should be sent to the child.
communicate() returns a tuple (stdout, stderr). <<<---
File: /usr/lib/python2.7/subprocess.py
Type: instancemethod
Running Python 2.6.6 and whenever I try to use 2 variables which are paths in another variable, I get a whitespace error:
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.
This is my code and the issue is with the cmd variable:
from subprocess import call, Popen, PIPE, STDOUT
example = '"C:\\Program Files\\Example\\test.cmd"'
output = '"C:\\test\\python\\reportFromPython.xml"'
cmd = example + " -T 'testing title' " + output
p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
output = p.stdout.read()
print output
If I change
cmd = example + " -T 'testing title' " + output
to
cmd = example + " -T 'testing title' "
Then it works but I need the output portion... How can I get it working with both variables?
According to this answer, you don't need shell=True if you're running a .cmd file. Then you can pass in your arguments as a list:
cmd = [example, "-T", "'testing title'", output]
And the rest of the code would be the same except for the removal of shell=True.
I am trying to replicate this command using python and Popen:
echo "Acct-Session-Id = 'E4FD590583649358F3B712'" | /usr/local/freeradius/bin/radclient -r 1 1.1.1.1:3799 disconnect secret
When running this from the command line as it is above, I get the expected:
Sent Disconnect-Request Id 17 from 0.0.0.0:59887 to 1.1.1.1:3799 length 44
I want to achieve the same from a python script, so I coded it like this:
rp1 = subprocess.Popen(["echo", "Acct-Session-Id = 'E4FD590583649358F3B712'"], stdout=subprocess.PIPE)
rp2 = subprocess.Popen(["/usr/local/freeradius/bin/radclient",
"-r 1",
"1.1.1.1:3799",
"disconnect",
"secret"],
stdin = rp1.stdout,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE)
rp1.stdout.close()
result = rp2.communicate()
print "RESULT: " + str(result)
But, I must be doing this incorrectly as the "result" variable contains the radclient usage info, as if it is called incorrectly:
RESULT: ('', "Usage: radclient [options] server[:port] <command> [<secret>]\n <command>....
Anybody any idea where my mistake lies?
Thanks!
Besides #Rawing catch of the args typo, you can make it much simpler with a single Popen process. Try this:
rp = subprocess.Popen(["/usr/local/freeradius/bin/radclient",
"-r",
"1",
"1.1.1.1:3799",
"disconnect",
"secret"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
result = rp.communicate("Acct-Session-Id = 'E4FD590583649358F3B712'")
Using communicate to handle all the I/O prevents possible deadlocks that are possible when explicitly writing to stdin when you also need to read from stdout/stderr.
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()
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.
I know this is an old topic, but there is a solution now. Call the rsync with option --outbuf=L. Example:
cmd=['rsync', '-arzv','--backup','--outbuf=L','source/','dest']
p = subprocess.Popen(cmd,
stdout=subprocess.PIPE)
for line in iter(p.stdout.readline, b''):
print '>>> {}'.format(line.rstrip())
Depending on the use case, you might also want to disable the buffering in the subprocess itself.
If the subprocess will be a Python process, you could do this before the call:
os.environ["PYTHONUNBUFFERED"] = "1"
Or alternatively pass this in the env argument to Popen.
Otherwise, if you are on Linux/Unix, you can use the stdbuf tool. E.g. like:
cmd = ["stdbuf", "-oL"] + cmd
See also here about stdbuf or other options.
On Linux, I had the same problem of getting rid of the buffering. I finally used "stdbuf -o0" (or, unbuffer from expect) to get rid of the PIPE buffering.
proc = Popen(['stdbuf', '-o0'] + cmd, stdout=PIPE, stderr=PIPE)
stdout = proc.stdout
I could then use select.select on stdout.
See also https://unix.stackexchange.com/questions/25372/
for line in p.stdout:
...
always blocks until the next line-feed.
For "real-time" behaviour you have to do something like this:
while True:
inchar = p.stdout.read(1)
if inchar: #neither empty string nor None
print(str(inchar), end='') #or end=None to flush immediately
else:
print('') #flush for implicit line-buffering
break
The while-loop is left when the child process closes its stdout or exits.
read()/read(-1) would block until the child process closed its stdout or exited.
Your problem is:
for line in p.stdout:
print(">>> " + str(line.rstrip()))
p.stdout.flush()
the iterator itself has extra buffering.
Try doing like this:
while True:
line = p.stdout.readline()
if not line:
break
print line
You cannot get stdout to print unbuffered to a pipe (unless you can rewrite the program that prints to stdout), so here is my solution:
Redirect stdout to sterr, which is not buffered. '<cmd> 1>&2' should do it. Open the process as follows: myproc = subprocess.Popen('<cmd> 1>&2', stderr=subprocess.PIPE)
You cannot distinguish from stdout or stderr, but you get all output immediately.
Hope this helps anyone tackling this problem.
To avoid caching of output you might wanna try pexpect,
child = pexpect.spawn(launchcmd,args,timeout=None)
while True:
try:
child.expect('\n')
print(child.before)
except pexpect.EOF:
break
PS : I know this question is pretty old, still providing the solution which worked for me.
PPS: got this answer from another question
p = subprocess.Popen(command,
bufsize=0,
universal_newlines=True)
I am writing a GUI for rsync in python, and have the same probelms. This problem has troubled me for several days until i find this in pyDoc.
If universal_newlines is True, the file objects stdout and stderr are opened as text files in universal newlines mode. Lines may be terminated by any of '\n', the Unix end-of-line convention, '\r', the old Macintosh convention or '\r\n', the Windows convention. All of these external representations are seen as '\n' by the Python program.
It seems that rsync will output '\r' when translate is going on.
if you run something like this in a thread and save the ffmpeg_time property in a property of a method so you can access it, it would work very nice
I get outputs like this:
output be like if you use threading in tkinter
input = 'path/input_file.mp4'
output = 'path/input_file.mp4'
command = "ffmpeg -y -v quiet -stats -i \"" + str(input) + "\" -metadata title=\"#alaa_sanatisharif\" -preset ultrafast -vcodec copy -r 50 -vsync 1 -async 1 \"" + output + "\""
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=True)
for line in self.process.stdout:
reg = re.search('\d\d:\d\d:\d\d', line)
ffmpeg_time = reg.group(0) if reg else ''
print(ffmpeg_time)
Change the stdout from the rsync process to be unbuffered.
p = subprocess.Popen(cmd,
shell=True,
bufsize=0, # 0=unbuffered, 1=line-buffered, else buffer-size
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
I've noticed that there is no mention of using a temporary file as intermediate. The following gets around the buffering issues by outputting to a temporary file and allows you to parse the data coming from rsync without connecting to a pty. I tested the following on a linux box, and the output of rsync tends to differ across platforms, so the regular expressions to parse the output may vary:
import subprocess, time, tempfile, re
pipe_output, file_name = tempfile.TemporaryFile()
cmd = ["rsync", "-vaz", "-P", "/src/" ,"/dest"]
p = subprocess.Popen(cmd, stdout=pipe_output,
stderr=subprocess.STDOUT)
while p.poll() is None:
# p.poll() returns None while the program is still running
# sleep for 1 second
time.sleep(1)
last_line = open(file_name).readlines()
# it's possible that it hasn't output yet, so continue
if len(last_line) == 0: continue
last_line = last_line[-1]
# Matching to "[bytes downloaded] number% [speed] number:number:number"
match_it = re.match(".* ([0-9]*)%.* ([0-9]*:[0-9]*:[0-9]*).*", last_line)
if not match_it: continue
# in this case, the percentage is stored in match_it.group(1),
# time in match_it.group(2). We could do something with it here...
In Python 3, here's a solution, which takes a command off the command line and delivers real-time nicely decoded strings as they are received.
Receiver (receiver.py):
import subprocess
import sys
cmd = sys.argv[1:]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
for line in p.stdout:
print("received: {}".format(line.rstrip().decode("utf-8")))
Example simple program that could generate real-time output (dummy_out.py):
import time
import sys
for i in range(5):
print("hello {}".format(i))
sys.stdout.flush()
time.sleep(1)
Output:
$python receiver.py python dummy_out.py
received: hello 0
received: hello 1
received: hello 2
received: hello 3
received: hello 4
I wrote a script to run a command-line program with different input arguments and grab a certain line from the output. I have the following running in a loop:
p1 = subprocess.Popen(["program", args], stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=False)
p2 = subprocess.Popen(["grep", phrase], stdin=p1.stdout, stdout=subprocess.PIPE, shell=False)
p1.wait()
p2.wait()
p = str(p2.stdout.readlines())
print 'p is ', p
One problem is that there is only output after the loop is finished running. I want to print something each time a process is finished. How can I do that?
Also, I want to have the option of displaying the output of p1. But I can't grab it with p1.stdout.readlines() without breaking p2. How can I do this?
I was thinking that I could just not make the call to grep, store the output of p1 and search for the phrase, but there's a lot of output, so this way seems pretty inefficient.
Any suggestions would be greatly appreciated. Thanks!
Here's a quick hack that worked for me on Linux. It might work for you, depending on your requirements. It uses tee as a filter that, if you pass print_all to your script, will duplicate an extra copy to /dev/tty (hey, I said it was a hack):
#!/usr/bin/env python
import subprocess
import sys
phrase = "bar"
if len(sys.argv) > 1 and sys.argv[1] == 'print_all':
tee_args = ['tee', '/dev/tty']
else:
tee_args = ['tee']
p1 = subprocess.Popen(["./program"], stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=False)
p2 = subprocess.Popen(tee_args, stdin=p1.stdout, stdout=subprocess.PIPE, shell=False)
p3 = subprocess.Popen(["grep", phrase], stdin=p2.stdout, stdout=subprocess.PIPE, shell=False)
p1.wait()
p2.wait()
p3.wait()
p = str(p3.stdout.readlines())
print 'p is ', p
With the following as contents for program:
#!/bin/sh
echo foo
echo bar
echo baz
Example output:
$ ./foo13.py
p is ['bar\n']
$ ./foo13.py print_all
foo
bar
baz
p is ['bar\n']
Try calling sys.stdout.flush() after each print statement.