This question already has answers here:
Test if executable exists in Python?
(15 answers)
Closed 4 years ago.
How do I check if a program exists from a python script?
Let's say you want to check if wget or curl are available. We'll assume that they should be in path.
It would be the best to see a multiplatform solution but for the moment, Linux is enough.
Hints:
running the command and checking for return code is not always enough as some tools do return non 0 result even when you try --version.
nothing should be visible on screen when checking for the command
Also, I would appreciate a solution that that is more general, like is_tool(name)
shutil.which
Let me recommend an option that has not been discussed yet: a Python implementation of which, specifically shutil.which. It was introduced in Python 3.3 and is cross-platform, supporting Linux, Mac, and Windows. It is also available in Python 2.x via whichcraft. You can also just rip the code for which right out of whichcraft here and insert it into your program.
def is_tool(name):
"""Check whether `name` is on PATH and marked as executable."""
# from whichcraft import which
from shutil import which
return which(name) is not None
distutils.spawn.find_executable
Another option that has already been mentioned is distutils.spawn.find_executable.
find_executable's docstring is as follows:
Tries to find 'executable' in the directories listed in 'path'
So if you pay attention, you'll note that the name of the function is somewhat misleading. Unlike which, find_executable does not actually verify that executable is marked as executable, only that it is on the PATH. So it's entirely possible (however unlikely) that find_executable indicates a program is available when it is not.
For example, suppose you have a file /usr/bin/wget that is not marked executable. Running wget from the shell will result in the following error: bash: /usr/bin/wget: Permission denied. which('wget') is not None will return False, yet find_executable('wget') is not None will return True. You can probably get away with using either function, but this is just something to be aware of with find_executable.
def is_tool(name):
"""Check whether `name` is on PATH."""
from distutils.spawn import find_executable
return find_executable(name) is not None
The easiest way is to try to run the program with the desired parameters, and handle the exception if it doesn't exist:
try:
subprocess.call(["wget", "your", "parameters", "here"])
except FileNotFoundError:
# handle file not found error.
This is a common pattern in Python: EAFP
In Python 2, you had to catch OsError instead, since the more fine-grained exception classes for OS errors did not exist yet:
try:
subprocess.call(["wget", "your", "parameters", "here"])
except OSError as e:
if e.errno == errno.ENOENT:
# handle file not found error.
else:
# Something else went wrong while trying to run `wget`
raise
You could use a subprocess call to the binary needed with :
"which" : *nix
"where" : Win 2003 and later (Xp has an addon)
to get the executable path (supposing it is in the environment path).
import os
import platform
import subprocess
cmd = "where" if platform.system() == "Windows" else "which"
try:
subprocess.call([cmd, your_executable_to_check_here])
except:
print "No executable"
or just use Ned Batchelder's wh.py script, that is a "which" cross platform implementation:
http://nedbatchelder.com/code/utilities/wh_py.html
import subprocess
import os
def is_tool(name):
try:
devnull = open(os.devnull)
subprocess.Popen([name], stdout=devnull, stderr=devnull).communicate()
except OSError as e:
if e.errno == os.errno.ENOENT:
return False
return True
I would probably shell out to which wget or which curl and check that the result ends in the name of the program you are using. The magic of unix :)
Actually, all you need to do is check the return code of which. So... using our trusty subprocess module:
import subprocess
rc = subprocess.call(['which', 'wget'])
if rc == 0:
print('wget installed!')
else:
print('wget missing in path!')
Note that I tested this on windows with cygwin... If you want to figure out how to implement which in pure python, i suggest you check here: http://pypi.python.org/pypi/pycoreutils (oh dear - it seems they don't supply which. Time for a friendly nudge?)
UPDATE: On Windows, you can use where instead of which for a similar effect.
I'd go for:
import distutils.spawn
def is_tool(name):
return distutils.spawn.find_executable(name) is not None
I'd change #sorin's answer as follows, the reason is it would check the name of the program without passing the absolute path of the program
from subprocess import Popen, PIPE
def check_program_exists(name):
p = Popen(['/usr/bin/which', name], stdout=PIPE, stderr=PIPE)
p.communicate()
return p.returncode == 0
import os
import subprocess
def is_tool(prog):
for dir in os.environ['PATH'].split(os.pathsep):
if os.path.exists(os.path.join(dir, prog)):
try:
subprocess.call([os.path.join(dir, prog)],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
except OSError, e:
return False
return True
return False
A slight modification to #SvenMarnach's code that addresses the issue of printing to the standard output stream. If you use the subprocess.check_output() function rather than subprocess.call() then you can handle the string that is normally printed to standard out in your code and still catch exceptions and the exit status code.
If you want to suppress the standard output stream in the terminal, don’t print the std out string that is returned from check_output:
import subprocess
import os
try:
stdout_string = subprocess.check_output(["wget", "--help"], stderr=subprocess.STDOUT)
# print(stdout_string)
except subprocess.CalledProcessError as cpe:
print(cpe.returncode)
print(cpe.output)
except OSError as e:
if e.errno == os.errno.ENOENT:
print(e)
else:
# Something else went wrong while trying to run `wget`
print(e)
The non-zero exit status code and output string are raised in the CalledProcessError as subprocess.CalledProcessError.returncode and subprocess.CalledProcessError.output so you can do whatever you'd like with them.
If you want to print the executable's standard output to the terminal, print the string that is returned:
import subprocess
import os
try:
stdout_string = subprocess.check_output(["wget", "--help"], stderr=subprocess.STDOUT)
print(stdout_string)
except subprocess.CalledProcessError as cpe:
print(cpe.returncode)
print(cpe.output)
except OSError as e:
if e.errno == os.errno.ENOENT:
print(e)
else:
# Something else went wrong while trying to run `wget`
print(e)
print() adds an extra newline to the string. If you want to eliminate that (and write std error to the std err stream instead of the std out stream as shown with the print() statements above), use sys.stdout.write(string) and sys.stderr.write(string) instead of print():
import subprocess
import os
import sys
try:
stdout_string = subprocess.check_output(["bogus"], stderr=subprocess.STDOUT)
sys.stdout.write(stdout_string)
except subprocess.CalledProcessError as cpe:
sys.stderr.write(cpe.returncode)
sys.stderr.write(cpe.output)
except OSError as e:
if e.errno == os.errno.ENOENT:
sys.stderr.write(e.strerror)
else:
# Something else went wrong while trying to run `wget`
sys.stderr.write(e.strerror)
Related
I'm trying to run some commands using the python library subprocess. Some of my commands might get stuck in loops and block the python script. Therefore I'm using check_output() with a timeout argument to limit the commands in time. When the command takes too much time, the function raise a TimeoutExpired error. What I want to do is get what the command has been able to run before being killed by the timeout.
I've except the error and tried "except sp.TimeoutExpired as e:". I read on the doc that if I do e.output it should give me what I want. "Output of the child process if this exception is raised by check_output(). Otherwise, None.". However I don't get anything in the output.
Here is what I did:
import subprocess as sp
try:
out = sp.check_output('ls', stderr=sp.STDOUT, universal_newlines=True, timeout=1)
except sp.TimeoutExpired as e:
print ('output: '+ e.output)
else:
return out
Let say the folder I'm working with is huge and so 1 second isn't enough to ls all its files. Therefore the TimeoutExpired error will be raised. However, I'd like to store what the script was able to get at least. Does someone have an idea?
Found a solution, posting it here in case someone is interested.
In Python 3, the run method allows to get the output.
Using the parameters as shown in the example, TimeoutExpired returns the output before the timeout in stdout:
import subprocess as sp
for cmd in [['ls'], ['ls', '/does/not/exist'], ['sleep', '5']]:
print('Running', cmd)
try:
out = sp.run(cmd, timeout=3, check=True, stdout=sp.PIPE, stderr=sp.STDOUT)
except sp.CalledProcessError as e:
print(e.stdout.decode() + 'Returned error code ' + str(e.returncode))
except sp.TimeoutExpired as e:
print(e.stdout.decode() + 'Timed out')
else:
print(out.stdout.decode())
Possible output:
Running ['ls']
test.py
Running ['ls', '/does/not/exist']
ls: cannot access '/does/not/exist': No such file or directory
Returned error code 2
Running ['sleep', '5']
Timed out
I hope it helps someone.
Use case:
Have to work with python subprocess32 module, for timeouts, but the code may run on windows as well. The module documentation suggests
if os.name == 'posix' and sys.version_info[0] < 3:
import subprocess32 as subprocess
else:
import subprocess
The problem:
I think the above method does not take into account that the methods like communicate, check_output, wait have timeout parameter only with subprocess32 module.
All calls to those will fail with this method
I don't wish to implement 2 different variants of the same function, conditionally import modules and everything.
Looking for a pythonic way of handling this. My hunch says that decorators and partial functions should help but can't seem to figure the precise and concise way.
Any suggestions ?
I devised a super ugly way of doing this using partial functions
from functools import partial
from subprocess import check_output
import subprocess
if os.name == 'posix' and sys.version_info[0] < 3:
from subprocess32 import check_output
import subprocess32 as subprocess
check_output = partial(check_output,timeout=10)
def execute_cmd(cmd, args):
command = []
command.append(cmd)
command = command + args
try:
proc_out = check_output(command, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
print("Failed to execute local command \nError code:%s, Output:%s",
e.cmd, e.returncode, e.output)
# I wish to handle TimeoutExpired exception here, but then this won't be generic
except:
print("Command %s failed to execute on host", command)
raise
How can I get the exist status (if command succeed or not) of any line in Python?
For example in bash, $? will tell me the last exist status of any command.
I need it to know if my connection to FTP server was successful or not.
Have you tried using a try/catch? If there was an error while executing the command, an exception will be raised. You can retrieve it with the sys module.
Example code:
import sys
try:
run_command()
except:
e = sys.exc_info()[0]
print(e)
If it is a function you call, it should have a retrun code you can collect like
retVal = doSomething()
Then you can check what happened.
I need to open a file in a specific application using python. I'm going to default to opening the file using the default app location / filename; however should the app be unable to be opened, I'd like to handle that error and give the user some other options.
I've understood so far that subprocess.call is the best way to do this, as opposed to system.os
Application: /Applications/GreatApp.app
This works fine
subprocess.call(['open','/Applications/GreatApp.app','placeholder.gap'])
However, when I start adding the try / except loops, they seem to do nothing. (note the space in the application name - I'm using the incorrect name to force an exception)
try:
subprocess.call(['open','/Applications/Great App.app','placeholder.gap'])
except:
print 'this is an error placeholder'
I'll still see the following error displayed in python
The file /Applications/Great App.app does not exist.
The closest I've found to some form of error handling is the following. Is looking at the value of retcode the right way to go about this?
try:
retcode = subprocess.call("open " + filename, shell=True)
if retcode < 0:
print >>sys.stderr, "Child was terminated by signal", -retcode
else:
print >>sys.stderr, "Child returned", retcode
except OSError, e:
print >>sys.stderr, "Execution failed:", e
Turns out retcode isn't the way, as both correct and incorrect names give a value greater than 0.
What does this show?
subprocess.call (['ls', '-l', '/Applications'])
The error message you got says that the application that you are trying to open does not exist.
And you won't get an exception if it doesn't as you have found.
Try with this. It will open file with its default editor, if it is installed.
ss=subprocess.Popen(FileName,shell=True)
ss.communicate()
I have no control over the application that the user has associated with the file I'm opening.
I need to be able to specify the application to be used and the file to be opened.
subprocess.call doesn't return exceptions should the application/file be unable to be opened.
It's friend subprocess.check_call, does.
From the docs: http://docs.python.org/2/library/subprocess.html#subprocess.check_call
If the return code was zero then return, otherwise raise
CalledProcessError. The CalledProcessError object will have the return
code in the returncode attribute.
I've provide my usage examples below for future reference
For OSX
FNULL = open(os.devnull, 'w') # Used in combination with the stdout variable
# to prevent output from being displayed while
# the program launches.
try:
# First try the default install location of the application
subprocess.check_call(['open','/Applications/Application.app',filename], stdout=FNULL)
except subprocess.CalledProcessError:
# Then ask user to manually enter in the filepath to Application.app
print 'unable to find /Applications/Application.app'
# Now ask user to manually enter filepath
For Windows, change this line
subprocess.check_call(['C:\file\to\program',filename], stdout=FNULL)
I'm currently converting a shell script to python and I'm having a problem. The current script uses the results of the last ran command like so.
if [ $? -eq 0 ];
then
testPassed=$TRUE
else
testPassed=$FALSE
fi
I have the if statement converted over just not sure about the $? part. As I am new to python I'm wondering if there is a similar way to do this?
You should look into the subprocess module for that. There is a check_call method for looking into exit codes (this is one method, there are others as well). As the manual mentions:
Run command with arguments. Wait for command to complete. If the
return code was zero then return, otherwise raise CalledProcessError.
The CalledProcessError object will have the return code in the
returncode attribute
An example of this is:
import subprocess
command=["ls", "-l"]
try:
exit_code=subprocess.check_call(command)
# Do something for successful execution here
print("Program run")
except subprocess.CalledProcessError as e:
print "Program exited with exit code", e.returncode
# Do something for error here
This will also include output, which you can either redirect to a file or suppress like so:
import subprocess
import os
command=["ls", "-l"]
try:
exit_code=subprocess.check_call(command, stdout=open(os.devnull, "w"))
# Do something for successful execution here
print("Program run")
except subprocess.CalledProcessError as e:
print "Program exited with exit code", e.returncode
# Do something for error here
Here is an example of a call with a non-zero exit code:
import subprocess
import os
command=["grep", "mystring", "/home/cwgem/testdir/test.txt"]
try:
exit_code=subprocess.check_call(command, stdout=open(os.devnull, "w"))
# Do something for successful execution here
print("Program run")
except subprocess.CalledProcessError as e:
print "Program exited with exit code", e.returncode
# Do something for error here
Output:
$ python process_exitcode_test.py
Program exited with exit code 1
Which is captured as an exception that you can handle as above. Note that this will not handle exceptions such as access denied or file not found. You will need to handle them on your own.
You might want use the sh module. It makes shell scripting in Python much more pleasant:
import sh
try:
output = sh.ls('/some/nen-existant/folder')
testPassed = True
except ErrorReturnCode:
testPassed = False