I am currently using
p1 = subprocess.Popen(['ps', 'ax'], stdout=subprocess.PIPE)
p2 = subprocess.Popen(['grep', 'bash'], stdin=p1.stdout, stdout=subprocess.PIPE)
p3 = subprocess.Popen(['wc', '-l'], stdin=p2.stdout, stdout=subprocess.PIPE)
count = int(p3.stdout.read())
if count == 2:
print count
print "yes"
else:
print "fail"
For checking to see if my python is currently running already or not.
It works fine.
However, I want to know if I can combine p1 p2 and p3 into one line.
I tried
p = subprocess.Popen(['ps ax | grep bash | wc -l'], stdout=subprocess.PIPE)
and few more but its not working.Is there a way to combine all these 3 into one line?
You can only use shell-style pipes (|) if you pass the shell=True keyword argument to Popen:
p = subprocess.Popen('ps ax | grep bash | wc -l', stdout=subprocess.PIPE, shell=True)
Otherwise, the entire string you pass is treated like the name of the executable, which will fail, since you don't have a program named ps ax | grep bash | wc -l.
Additionally, you shouldn't pass the command as a list with shell=True, you just pass a string.
One other note: You probably need to adjust your command for this to work reliably, so that the grep bash command itself isn't counted in the output of ps ax | grep bash. One trick to do this is to use ps ax | grep [b]ash. Also, using shell=True might end up starting a bash instance to run the command, so you might need to use if count == 3 instead of if count == 2.
Putting it altogether:
p = subprocess.Popen('ps ax | grep [b]ash | wc -l', stdout=subprocess.PIPE, shell=True)
count = int(p.stdout.read())
if count == 2: # Or maybe 3?
print count
print "yes"
else:
print "fail"
Edit:
Here's output from running different versions of the code on my machine
>>> p1 = subprocess.Popen(['ps', 'ax'], stdout=subprocess.PIPE)
>>> p2 = subprocess.Popen(['grep', 'bash'], stdin=p1.stdout, stdout=subprocess.PIPE)
>>> p3 = subprocess.Popen(['wc', '-l'], stdin=p2.stdout, stdout=subprocess.PIPE); p3.stdout.read()
'42\n'
>>> p1 = subprocess.Popen(['ps', 'ax'], stdout=subprocess.PIPE)
>>> p2 = subprocess.Popen(['grep', 'bash'], stdin=p1.stdout, stdout=subprocess.PIPE)
>>> p3 = subprocess.Popen(['wc', '-l'], stdin=p2.stdout, stdout=subprocess.PIPE); p3.stdout.read()
'42\n'
>>> p1 = subprocess.Popen(['ps', 'ax'], stdout=subprocess.PIPE)
>>> p2 = subprocess.Popen(['grep', 'bash'], stdin=p1.stdout, stdout=subprocess.PIPE)
>>> p3 = subprocess.Popen(['wc', '-l'], stdin=p2.stdout, stdout=subprocess.PIPE); p3.stdout.read()
'42\n'
>>> p1 = subprocess.Popen(['ps', 'ax'], stdout=subprocess.PIPE)
>>> p2 = subprocess.Popen(['grep', 'bash'], stdin=p1.stdout, stdout=subprocess.PIPE)
>>> p3 = subprocess.Popen(['wc', '-l'], stdin=p2.stdout, stdout=subprocess.PIPE); p3.stdout.read()
'42\n'
>>> p1 = subprocess.Popen(['ps', 'ax'], stdout=subprocess.PIPE)
>>> p2 = subprocess.Popen(['grep', 'bash'], stdin=p1.stdout, stdout=subprocess.PIPE)
>>> p3 = subprocess.Popen(['wc', '-l'], stdin=p2.stdout, stdout=subprocess.PIPE); p3.stdout.read()
'42\n'
>>> p = subprocess.Popen('ps -ef | grep bash | wc -l ', stdout=subprocess.PIPE, shell=True) ; print(''.join(p.stdout.readlines()))
44
>>> p = subprocess.Popen('ps -ef | grep bash | wc -l ', stdout=subprocess.PIPE, shell=True) ; print(''.join(p.stdout.readlines()))
44
>>> p = subprocess.Popen('ps -ef | grep bash | wc -l ', stdout=subprocess.PIPE, shell=True) ; print(''.join(p.stdout.readlines()))
44
>>> p = subprocess.Popen('ps -ef | grep [b]ash | wc -l ', stdout=subprocess.PIPE, shell=True) ; print(''.join(p.stdout.readlines()))
42
>>> p = subprocess.Popen('ps -ef | grep [b]ash | wc -l ', stdout=subprocess.PIPE, shell=True) ; print(''.join(p.stdout.readlines()))
42
>>> p = subprocess.Popen('ps -ef | grep [b]ash | wc -l ', stdout=subprocess.PIPE, shell=True) ; print(''.join(p.stdout.readlines()))
42
Here is the obvious answer. All I did was replace the second p1 and p2 with the first assignment.
p3 = subprocess.Popen(['wc', '-l'], stdin=subprocess.Popen(['grep', 'bash'], \
stdin=subprocess.Popen(['ps', 'ax'], stdout=subprocess.PIPE).stdout, \
stdout=subprocess.PIPE).stdout, stdout=subprocess.PIPE)
Related
If I do the following:
>>> cmd='echo -n "1\n2"'
>>> proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8')
>>> proc.stdout.read()
'1\n2'
but if I use a shell script:
cat test.sh
#!/bin/bash
echo -n "1\n2"
>>> cmd='sh test.sh'
>>> proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8')
>>> proc.stdout.read()
'1\\n2'
and I change \n to \t
cat test.sh
#!/bin/bash
echo -n "1\t2"
>>> cmd='sh test.sh'
>>> proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8')
>>> proc.stdout.read()
'1\\t2'
Why does using a script get two \ ?
In the Python 2.7 documentation of subprocess module, it provides a simple solution to replace the shell pipeline:
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]
Source: https://docs.python.org/2/library/subprocess.html#replacing-shell-pipeline
I want to have a wrapper function that can support multiple | in command.
Yes, the easiest solution is to set Shell=True which makes the program vulnerable to shell injection.
I'm wondering if there is a neat way to support both multiple | and keep Shell=False
I'm wondering if there is a neat way to support both multiple | and keep shell=False
Yes, the two-command example from the documentation works for any number of commands in the pipeline:
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()
p3 = Popen(["grep", "-v", "audio"], stdin=p2.stdout, stdout=PIPE)
p2.stdout.close()
output = p3.communicate()[0]
For clarity and convenience, this can be extracted to a utility function:
import subprocess
def pipe(*arglists):
prev_proc = subprocess.Popen(arglists[0], stdout=subprocess.PIPE)
for args in arglists[1:]:
proc = subprocess.Popen(args, stdin=prev_proc.stdout, stdout=subprocess.PIPE)
prev_proc.stdout.close()
prev_proc = proc
return prev_proc
>>> p = pipe(["dmesg"], ["grep", "hda"], ["grep", "-v", "audio"])
>>> p.communicate()
('[ 2.654606] snd_hda_intel 0000:0f:00.1: Disabling MSI\n', None)
I need to run the following (working) command in Python:
ip route list dev eth0 | awk ' /^default/ {print $3}'
Using subprocess, I would have to do the following:
first = "ip route list dev eth0"
second = "awk ' /^default/ {print $3}'"
p1 = subprocess.Popen(first.split(), stdout=subprocess.PIPE)
p2 = subprocess.Popen(second.split(), stdin=p1.stdout, stdout=subprocess.PIPE)
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]
Something went wrong with p2. I get:
>>> awk: cmd. line:1: '
awk: cmd. line:1: ^ invalid char ''' in expression
What should I do? On a terminal it works perfectly.
split splits on any whitespace, including that inside single-quoted arguments. If you really have to, use shlex.split:
import shlex
p2 = subprocess.Popen(shlex.split(second), stdin=p1.stdout, stdout=subprocess.PIPE)
However it usually makes more sense to specify the commands directly:
first = ['ip', 'route', 'list', 'dev', 'eth0']
second = ['awk', ' /^default/ {print $3}']
p1 = subprocess.Popen(first, stdout=subprocess.PIPE)
p2 = subprocess.Popen(second, stdin=p1.stdout, stdout=subprocess.PIPE)
Not the best solution, but while you are waiting for the best answer, you can still do this :
cmd = "ip route list dev eth0 | awk ' /^default/ {print $3}'"
p2 = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
This question already has answers here:
How do I use subprocess.Popen to connect multiple processes by pipes?
(9 answers)
Closed 8 years ago.
I'd like to use subprocess on the following line:
convert ../loxie-orig.png bmp:- | mkbitmap -f 2 -s 2 -t 0.48 | potrace -t 5 --progress -s -o ../DSC00232.svg
I found thank to other posts the subprocess documentation but in the example we use only twice pipe.
So, I try for two of the three commands and it works
p1 = subprocess.Popen(['convert', fileIn, 'bmp:-'], stdout=subprocess.PIPE)
# p2 = subprocess.Popen(['mkbitmap', '-f', '2', '-s', '2', '-t', '0.48'], stdout=subprocess.PIPE)
p3 = subprocess.Popen(['potrace', '-t' , '5', '-s' , '-o', fileOut], stdin=p1.stdout,stdout=subprocess.PIPE)
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p3 exits.
output = p3.communicate()[0]
Can you help me for the third command?
Thank you very much.
Just add a third command following the same example:
p1 = subprocess.Popen(['convert', fileIn, 'bmp:-'], stdout=subprocess.PIPE)
p2 = subprocess.Popen(['mkbitmap', '-f', '2', '-s', '2', '-t', '0.48'],
stdin=p1.stdout, stdout=subprocess.PIPE)
p1.stdout.close()
p3 = subprocess.Popen(['potrace', '-t' , '5', '-s' , '-o', fileOut],
stdin=p2.stdout,stdout=subprocess.PIPE)
p2.stdout.close()
output = p3.communicate()[0]
def runPipe(cmds):
try:
p1 = subprocess.Popen(cmds[0].split(' '), stdin = None, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
prev = p1
for cmd in cmds[1:]:
p = subprocess.Popen(cmd.split(' '), stdin = prev.stdout, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
prev = p
stdout, stderr = p.communicate()
p.wait()
returncode = p.returncode
except Exception, e:
stderr = str(e)
returncode = -1
if returncode == 0:
return (True, stdout.strip().split('\n'))
else:
return (False, stderr)
Then execute it like:
runPipe(['ls -1','head -n 2', 'head -n 1'])
Use subprocess.Popen() with the option shell=True, and you can pass it your entire command as a single string.
This is the simplest solution and makes it possible to embed a complicated pipeline in python without head-scratching; but in some cases it might not work, e.g. (as #torek commented) if there are spaces in the filenames passed for input or output. In that case, take the trouble to build up the robust solution in the accepted answer.
I know how to run a command using cmd = subprocess.Popen and then subprocess.communicate.
Most of the time I use a string tokenized with shlex.split as 'argv' argument for Popen.
Example with "ls -l":
import subprocess
import shlex
print subprocess.Popen(shlex.split(r'ls -l'), stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE).communicate()[0]
However, pipes seem not to work... For instance, the following example returns noting:
import subprocess
import shlex
print subprocess.Popen(shlex.split(r'ls -l | sed "s/a/b/g"'), stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE).communicate()[0]
Can you tell me what I am doing wrong please?
Thx
I think you want to instantiate two separate Popen objects here, one for 'ls' and the other for 'sed'. You'll want to pass the first Popen object's stdout attribute as the stdin argument to the 2nd Popen object.
Example:
p1 = subprocess.Popen('ls ...', stdout=subprocess.PIPE)
p2 = subprocess.Popen('sed ...', stdin=p1.stdout, stdout=subprocess.PIPE)
print p2.communicate()
You can keep chaining this way if you have more commands:
p3 = subprocess.Popen('prog', stdin=p2.stdout, ...)
See the subprocess documentation for more info on how to work with subprocesses.
I've made a little function to help with the piping, hope it helps. It will chain Popens as needed.
from subprocess import Popen, PIPE
import shlex
def run(cmd):
"""Runs the given command locally and returns the output, err and exit_code."""
if "|" in cmd:
cmd_parts = cmd.split('|')
else:
cmd_parts = []
cmd_parts.append(cmd)
i = 0
p = {}
for cmd_part in cmd_parts:
cmd_part = cmd_part.strip()
if i == 0:
p[i]=Popen(shlex.split(cmd_part),stdin=None, stdout=PIPE, stderr=PIPE)
else:
p[i]=Popen(shlex.split(cmd_part),stdin=p[i-1].stdout, stdout=PIPE, stderr=PIPE)
i = i +1
(output, err) = p[i-1].communicate()
exit_code = p[0].wait()
return str(output), str(err), exit_code
output, err, exit_code = run("ls -lha /var/log | grep syslog | grep gz")
if exit_code != 0:
print "Output:"
print output
print "Error:"
print err
# Handle error here
else:
# Be happy :D
print output
shlex only splits up spaces according to the shell rules, but does not deal with pipes.
It should, however, work this way:
import subprocess
import shlex
sp_ls = subprocess.Popen(shlex.split(r'ls -l'), stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
sp_sed = subprocess.Popen(shlex.split(r'sed "s/a/b/g"'), stdin = sp_ls.stdout, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
sp_ls.stdin.close() # makes it similiar to /dev/null
output = sp_ls.communicate()[0] # which makes you ignore any errors.
print output
according to help(subprocess)'s
Replacing shell pipe line
-------------------------
output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]
HTH
"""
Why don't you use shell
"""
def output_shell(line):
try:
shell_command = Popen(line, stdout=PIPE, stderr=PIPE, shell=True)
except OSError:
return None
except ValueError:
return None
(output, err) = shell_command.communicate()
shell_command.wait()
if shell_command.returncode != 0:
print "Shell command failed to execute"
return None
return str(output)
Thank #hernvnc, #glglgl, and #Jacques Gaudin for the answers. I fixed the code from #hernvnc. His version will cause hanging in some scenarios.
import shlex
from subprocess import PIPE
from subprocess import Popen
def run(cmd, input=None):
"""Runs the given command locally and returns the output, err and exit_code."""
if "|" in cmd:
cmd_parts = cmd.split('|')
else:
cmd_parts = []
cmd_parts.append(cmd)
i = 0
p = {}
for cmd_part in cmd_parts:
cmd_part = cmd_part.strip()
if i == 0:
if input:
p[i]=Popen(shlex.split(cmd_part),stdin=PIPE, stdout=PIPE, stderr=PIPE)
else:
p[i]=Popen(shlex.split(cmd_part),stdin=None, stdout=PIPE, stderr=PIPE)
else:
p[i]=Popen(shlex.split(cmd_part),stdin=p[i-1].stdout, stdout=PIPE, stderr=PIPE)
i = i +1
# close the stdin explicitly, otherwise, the following case will hang.
if input:
p[0].stdin.write(input)
p[0].stdin.close()
(output, err) = p[i-1].communicate()
exit_code = p[0].wait()
return str(output), str(err), exit_code
# test case below
inp = b'[ CMServer State ]\n\nnode node_ip instance state\n--------------------------------------------\n1 linux172 10.90.56.172 1 Primary\n2 linux173 10.90.56.173 2 Standby\n3 linux174 10.90.56.174 3 Standby\n\n[ ETCD State ]\n\nnode node_ip instance state\n--------------------------------------------------\n1 linux172 10.90.56.172 7001 StateFollower\n2 linux173 10.90.56.173 7002 StateLeader\n3 linux174 10.90.56.174 7003 StateFollower\n\n[ Cluster State ]\n\ncluster_state : Normal\nredistributing : No\nbalanced : No\ncurrent_az : AZ_ALL\n\n[ Datanode State ]\n\nnode node_ip instance state | node node_ip instance state | node node_ip instance state\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n1 linux172 10.90.56.172 6001 P Standby Normal | 2 linux173 10.90.56.173 6002 S Primary Normal | 3 linux174 10.90.56.174 6003 S Standby Normal'
cmd = "grep -E 'Primary' | tail -1 | awk '{print $3}'"
run(cmd, input=inp)