How do I place output of bash command to Python variable? - python

How do I place output of bash command to Python variable?
I am writing a Python script, which I want to enter the output of
bash command:
rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH} %{VENDOR}\n' | grep -v 'Red Hat'|wc -l, and place it to Python variable, let say R.
After that I want to do, Python if R != 0
then run some Linux command.
How do I achieve that?

There are various options, but the easiest is probably using subprocess.check_output() with shell=True although this can be security hazard if you don't fully control what command is passed in.
import subprocess
var = subprocess.check_output('rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH} %{VENDOR}\n' | grep -v 'Red Hat'|wc -l', shell = True)
var = int(var)
You need to use shell=True as otherwise the pipes would not be interpreted.
If you need more control you might want to look at plumbum where you can do:
from plumbum.cmd import rpm, grep, wc
chain = rpm["-qa", "--qf", r"%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH} %{VENDOR}\n"] | grep["-v", "Red Hat"] | wc["-l"]
R = int(chain())
Although I would probably not invoke wc and get the whole output and count its length within python (easier to check that you got just the lines that you expected, piping through wc -l throws away all of the details)

I would recommend envoy primarily because the API is much more intuitive to use for 90% of the use cases.
r = envoy.run('ls ', data='data to pipe in', timeout=2)
print r.status_code # returns status code
print r.std_out # returns the output.
See the Envoy Github page for more details.

You can use stdin.
#!/usr/bin/python
import sys
s = sys.stdin.read()
print s
Then you will run a bash command like this
echo "Hello" | ./myscript.py
Output
Hello

You can replace shell pipeline using Popen:
from subprocess import PIPE,Popen
p1 = Popen(["rpm", "-qa", "--qf", '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH} %{VENDOR}\n'],stdout=PIPE)
p2 = Popen(["grep", "-v", 'Red Hat'],stdin=p1.stdout,stdout=PIPE)
p1.stdout.close()
p3 = Popen(["wc", "-l"],stdin=p2.stdout,stdout=PIPE)
p2.stdout.close()
out,err = p3.communicate()
If you just want to check if grep returned any matches then forget the wc - l and just check what grep returns:
p1 = Popen(["rpm", "-qa", "--qf", '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH} %{VENDOR}\n'],stdout=PIPE)
p2 = Popen(["grep", "-v", 'Red Hat'],stdin=p1.stdout,stdout=PIPE)
p1.stdout.close()
out,err = p2.communicate()
if out:
...
Or just use check_output to run the rpm command and check the string for "Red Hat":
out = check_output(["rpm", "-qa", "--qf", '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH} %{VENDOR}\n'])
if "Red Hat" not in out:
....
Which is the same as inverting the search with grep -v then checking if there are any matches with wc.

Related

Store filtered output of cmd command in a variable

I am trying to store the output of a cmd command as a variable in python.
To achieve this i am using os.system() but os.system() just runs the process,it doesn't capture the output.
import os
PlatformName = os.system("adb shell getprop | grep -e 'bt.name'")
DeviceName = os.system("adb shell getprop | grep -e '.product.brand'")
DeviceID = os.system("adb shell getprop | grep -e 'serialno'")
Version = os.system("adb shell getprop | grep -e 'version.release'")
print(PlatformName)
print(DeviceName)
print(DeviceID)
print(Version)
Then i tried to use the subprocess module.
import subprocess
import os
PlatformName = subprocess.check_output(["adb shell getprop | grep -e 'bt.name'"])
DeviceName = subprocess.check_output(["adb shell getprop | grep -e '.product.brand'"])
DeviceID = subprocess.check_output(["adb shell getprop | grep -e 'serialno'"])
Version = subprocess.check_output(["adb shell getprop | grep -e 'version.release'"])
print(PlatformName)
print(DeviceName)
print(DeviceID)
print(Version)
I am getting the following error
FileNotFoundError: [WinError 2] The system cannot find the file
specified
How can I store the output of the command as a variable?
The issues here:
passing arguments like this (string in a list, with spaces) is really not recommended
passing arguments like this need shell=True for it to have a slight chance to work, and shell=True is known for security issues (and other issues as well, like non-portability)
grep is not standard on windows, and the pattern is a regex which means you'd probably have to escape . ("bt\.name").
when not found grep returns 1 and would make check_output fail.
when found grep returns match(es), and a newline, that you'd have to strip
I'd rewrite this:
PlatformName = subprocess.check_output(["adb shell getprop | grep -e 'bt.name'"])
as:
output = subprocess.check_output(["adb","shell","getprop"])
platform_name = next((line for line in output.decode().splitlines() if "bt.name" in line),"")
The second line is a "native" version of grep (without regexes). It returns the first occurrence of "bt.line" in the output lines or empty string if not found.
You don't need grep here (the above is not strictly equivalent, as it yields the first occurrence, not all the occurrences but that should be okay on your case). And your clients may not have grep installed on Windows.
Hey I got the same problem as you. Sub-process can do what you want even with the shell=False. The trick is the communicate() method.
with subprocess.Popen(cmdCode,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd = workingDir,
bufsize=1,
universal_newlines = True) as proc:
#output is stored in proc.stdout
#errors are stored in proc.stderr
Now you just need a little function to scan the proc.stdout for the information you need: PlatformName, etc

Cant use grep in subprocess command

I'm having a problem with my subprocess command, I like to grep out the lines that match with "Online" line.
def run_command(command):
p = subprocess.Popen(command,shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
return iter(p.stdout.readline, b'')
command = 'mosquitto_sub -u example -P example -t ITT/# -v | grep "Online" '.split()
for line in run_command(command):
print(line)
But I will get an error
Error: Unknown option '|'.
Use 'mosquitto_sub --help' to see usage.
But when running with linux shell
user#server64:~/Pythoniscriptid$ mosquitto_sub -u example -P example -t ITT/# -v | grep "Online"
ITT/C5/link Online
ITT/IoT/tester55/link Online
ITT/ESP32/TEST/link Online
I also tried shell = True, but with no success, because I will get another error, that dosen't recognize the topic ITT/#
Error: You must specify a topic to subscribe to.
Use 'mosquitto_sub --help' to see usage.
The "possible dublicate" didn't help me at all, So I think I'm having a different problem. I tried to change code to this, put in not getting any return
def run_command(command,command2):
p1 = subprocess.Popen(command,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
p2 = subprocess.Popen(command2,stdin=p1.stdout,stdout=subprocess.PIPE)
return iter(p2.stdout.readline,'')
command = 'mosquitto_sub -u example -P example -t ITT/# -v'.split()
command2 = 'grep Online'.split()
#subprocess.getoutput(command)
for line in run_command(command,command2):
print(line)
When you split the text, the list will look like
['mosquitto_sub', ..., 'ITT/#', '-v', '|', 'grep', '"Online"']
When you pass this list to subprocess.Popen, a literal '|' will be one of the arguments to mosquitto_sub.
If you use shell=True, you must escape any special characters like # in the command, for instance with double quotes:
import subprocess
command = 'echo -e "ITT/#\\ni am Online\\nbar Online\\nbaz" | grep "Online" '
p = subprocess.Popen(
command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(p.stdout.readline, b''):
print(line)
Alternatively, connect the pipes as you wrote, but make sure to iterate until b'', not u'':
import subprocess
def run_command(command, command2):
p1 = subprocess.Popen(command,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
p2 = subprocess.Popen(command2,stdin=p1.stdout,stdout=subprocess.PIPE)
return iter(p2.stdout.readline, b'')
command = ['echo', '-e', 'ITT/#\\ni am Online\\nbar Online\\nbaz']
command2 = 'grep Online'.split()
for line in run_command(command,command2):
print(line)

Python, evaluation output of OS command in an if statement

I want to convert the following shell evaluation to python2.6(can't upgrade). I can't figure out how to evaluate the output of the command.
Here's the shell version:
status=`$hastatus -sum |grep $hostname |grep Grp| awk '{print $6}'`
if [ $status != "ONLINE" ]; then
exit 1
fi
I tried os.popen and it returns ['ONLINE\n'].
value = os.popen("hastatus -sum |grep `hostname` |grep Grp| awk '{print $6}'".readlines()
print value
Try the subprocess module:
import subprocess
value = subprocess.call("hastatus -sum |grep `hostname` |grep Grp| awk '{print $6}'")
print(value)
Documentation is found here:
https://docs.python.org/2.6/library/subprocess.html?highlight=subprocess#module-subprocess
The recommended way is to use the subprocess module.
The following section of the documentation is instructive:
replacing shell pipeline
I report here for reference:
output=dmesg | grep hda
becomes:
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]
The p1.stdout.close() call after starting the p2 is important in order for p1 to receive a SIGPIPE if p2 exits before p1.
Alternatively, for trusted input, the shell’s own pipeline support may still be used directly:
output=dmesg | grep hda
becomes:
output=check_output("dmesg | grep hda", shell=True)
And here the recipe to translate os.popen to the subprocess module:
replacing os.popen()
So in your case you could do something like
import subprocess
output=check_output("hastatus -sum |grep `hostname` |grep Grp| awk '{print $6}'", shell=True)
or
concatenating the Popens as showed in the documentation above (probably what I would do).
Then to test the output you could just use, assuming you're using the first approach:
import sys
import subprocess
....
if 'ONLINE' in output:
sys.exit(1)

Convert complex command into python subprocess

I have the following command:
$ ffmpeg -i http://url/1video.mp4 2>&1 | perl -lane 'print $1 if /(\d+x\d+)/'
640x360
I'm trying to set the output of this command into a python variable. Here is what I have so far:
>>> from subprocess import Popen, PIPE
>>> p1 = Popen(['ffmpeg', '-i', 'http://url/1video.mp4', '2>&1'], stdout=PIPE)
>>> p2=Popen(['perl','-lane','print $1 if /(\d+x\d+)/'], stdin=p1.stdout, stdout=PIPE)
>>> dimensions = p2.communicate()[0]
''
What am I doing incorrectly here, and how would I get the correct value for dimensions?
In general, you can replace a shell pipeline with this pattern:
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]
However, in this case, no pipeline is necessary:
import subprocess
import shlex
import re
url='http://url/1video.mp4'
proc=subprocess.Popen(shlex.split('ffmpeg -i {f}'.format(f=url)),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
dimensions=None
for line in proc.stderr:
match=re.search(r'(\d+x\d+)',line)
if match:
dimensions=match.group(1)
break
print(dimensions)
No need to call perl from within python.
If you have the output from ffmpeg in a variable, you can do something like this:
print re.search(r'(\d+x\d+)', str).group()
Note the “shell” argument to subprocess.Popen: this specifies whether the command you pass is parsed by the shell or not.
That “2>&1” is one of those things that needs to be parsed by a shell, otherwise FFmpeg (like most programs) will try to treat it as a filename or option value.
The Python sequence that most closely mimics the original would probably be more like
p1 = subprocess.Popen("ffmpeg -i http://url/1video.mp4 2>&1", shell = True, stdout = subprocess.PIPE)<BR>
p2 = subprocess.Popen(r"perl -lane 'print $1 if /(\d+x\d+)/'", shell = True, stdin = p1.stdout, stdout = subprocess.PIPE)<BR>
dimensions = p2.communicate()[0]

Parsing a stdout in Python

In Python I need to get the version of an external binary I need to call in my script.
Let's say that I want to use Wget in Python and I want to know its version.
I will call
os.system( "wget --version | grep Wget" )
and then I will parse the outputted string.
How to redirect the stdout of the os.command in a string in Python?
One "old" way is:
fin,fout=os.popen4("wget --version | grep Wget")
print fout.read()
The other modern way is to use a subprocess module:
import subprocess
cmd = subprocess.Popen('wget --version', shell=True, stdout=subprocess.PIPE)
for line in cmd.stdout:
if "Wget" in line:
print line
Use the subprocess module:
from subprocess import Popen, PIPE
p1 = Popen(["wget", "--version"], stdout=PIPE)
p2 = Popen(["grep", "Wget"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]
Use subprocess instead.
If you are on *nix, I would recommend you to use commands module.
import commands
status, res = commands.getstatusoutput("wget --version | grep Wget")
print status # Should be zero in case of of success, otherwise would have an error code
print res # Contains stdout

Categories

Resources