Equivalent of Bash Backticks in Python
Question
What is the equivalent of the backticks found in Ruby and Perl in Python? That is, in Ruby I can do this:
foo = `cat /tmp/baz`
What does the equivalent statement look like in Python? I've tried os.system("cat /tmp/baz")
but that puts the result to standard out and returns to me the error code of that operation.
Solution
output = os.popen('cat /tmp/baz').read()
OTHER TIPS
The most flexible way is to use the subprocess
module:
import subprocess
out = subprocess.run(["cat", "/tmp/baz"], capture_output=True)
print("program output:", out)
capture_output
was introduced in Python 3.7, for older versions the special function check_output()
can be used instead:
out = subprocess.check_output(["cat", "/tmp/baz"])
You can also manually construct a subprocess object if you need fine grained control:
proc = subprocess.Popen(["cat", "/tmp/baz"], stdout=subprocess.PIPE)
(out, err) = proc.communicate()
All these functions support keyword parameters to customize how exactly the subprocess is executed. You can for example use shell=True
to execute the program through the shell, if you need things like file name expansions of *
, but that comes with limitations.
sth is right. You can also use os.popen(), but where available (Python 2.4+) subprocess is generally preferable.
However, unlike some languages that encourage it, it's generally considered bad form to spawn a subprocess where you can do the same job inside the language. It's slower, less reliable and platform-dependent. Your example would be better off as:
foo= open('/tmp/baz').read()
eta:
baz is a directory and I'm trying to get the contents of all the files in that directory
? cat on a directory gets me an error.
If you want a list of files:
import os
foo= os.listdir('/tmp/baz')
If you want the contents of all files in a directory, something like:
contents= []
for leaf in os.listdir('/tmp/baz'):
path= os.path.join('/tmp/baz', leaf)
if os.path.isfile(path):
contents.append(open(path, 'rb').read())
foo= ''.join(contents)
or, if you can be sure there are no directories in there, you could fit it in a one-liner:
path= '/tmp/baz'
foo= ''.join(open(os.path.join(path, child), 'rb').read() for child in os.listdir(path))
foo = subprocess.check_output(["cat", "/tmp/baz"])
From Python 3.5 onward, the recommended way is to use subprocess.run
. To get the same behaviour as you describe, you would use:
output = subprocess.run("ls", shell=True, stdout=subprocess.PIPE).stdout
This will return a bytes
object. You might want to append .decode("ascii")
or .decode("utf-8")
to the end to get a str
.
Easiest way is to use commands package.
import commands
commands.getoutput("whoami")
Output:
'bganesan'
import os
foo = os.popen('cat /tmp/baz', 'r').read()
I'm using
(6:0)$ python --version Python 2.7.1
One of the examples above is:
import subprocess
proc = subprocess.Popen(["cat", "/tmp/baz"], stdout=subprocess.PIPE, shell=True)
(out, err) = proc.communicate()
print "program output:", out
For me, this failed to access the directory /tmp. After looking at the doc string for subprocess I replaced
[ "prog", "arg"]
with
"prog arg"
and got the shell expansion behavior that was desired (a la Perl's `prog arg`)
print subprocess.Popen("ls -ld /tmp/v*", stdout=subprocess.PIPE, shell=True).communicate()[0]
I quit using python a while back because I was annoyed with the difficulty of of doing the equivalent of perl `cmd ...`. I'm glad to find Python has made this reasonable.
If you use subprocess.Popen, remember to specify bufsize. The default is 0, which means "unbuffered", not "choose a reasonable default".
This will not work in python3, but in python2 you can extend str
with a custom __repr__
method that calls your shell command and returns it like so:
#!/usr/bin/env python
import os
class Command(str):
"""Call system commands"""
def __repr__(cmd):
return os.popen(cmd).read()
Which you can use like
#!/usr/bin/env python
from command import Command
who_i_am = `Command('whoami')`
# Or predeclare your shell command strings
whoami = Command('whoami')
who_i_am = `whoami`
The backtick
(`) operator was removed in Python 3
. It is confusingly similar to a single quote, and hard to type on some keyboards. Instead of the backtick
, use the equivalent built-in function repr()
.