92

I need to setup environment with the path to a binary. In the shell, I can use which to find the path. Is there an equivalent in python? This is my code.

cmd = ["which","abc"]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
res = p.stdout.readlines()
if len(res) == 0: return False
return True
4
  • Even in the shell itself, which itself is not a good choice for detecting if a command is installed. Reference Commented May 5, 2015 at 18:26
  • Possible duplicate of Test if executable exists in Python? Commented Oct 10, 2018 at 13:21
  • Not sure if it's applicable but take a look at import sys; sys.executable. Commented Feb 15, 2024 at 8:00
  • That’s good for getting the absolute path of the Python interpreter that line was executed in. Quite useful for triggering other Python scripts, even though I’m not entirely sure that’s the best way of doing so… Commented Jan 14 at 1:09

7 Answers 7

102

I know this is an older question, but if you happen to be using Python 3.3+ you can use shutil.which(cmd). You can find the documentation here. It has the advantage of being in the standard library.

An example would be like so:

>>> import shutil
>>> shutil.which("bash")
'/usr/bin/bash'
Sign up to request clarification or add additional context in comments.

Comments

97

There is distutils.spawn.find_executable().

6 Comments

+1, this is cool and part of the standard library! Be aware that it's very limited on Windows - it doesn't parse PATHEXT, instead it assumes it should be searching for a '.exe' extension (missing batch files etc)
Beware it doesn't check whether file is executable.
This didn't work for me on 2.7 or 3.6. It gave an error that the spawn module wasn't found.
@temoto And yet despite that it won't find a dll file (that I can check is on PATH via where.exe) for me on Windows.
There is a huge bug in this function in that if there is a file in the current directory with the same name as the executable you're searching for, it returns the bare executable name (see this source line). As an example, if you run the following: $ touch bash; python3 -c "from distutils.spawn import find_executable; print(find_executable('bash'))" it prints out bash. Use shutil.which instead.
|
14

There's not a command to do that, but you can iterate over environ["PATH"] and look if the file exists, which is actually what which does.

import os

def which(file):
    for path in os.environ["PATH"].split(os.pathsep):
        if os.path.exists(os.path.join(path, file)):
                return os.path.join(path, file)

    return None

Good luck!

5 Comments

You want to be cautious making assumptions about the pathsep character.
and path separator, but this is just a quirk to make a point. Good luck!
use os.path.sep instead of / and os.pathsep instead of :
Do not use '+', use os.path.join. See more up-voted answers for a stdlib implementation (distutils) and a more platform-independent from the Twisted project.
thanks for os.path.join. The twisted implementation is completely isolated, doesn't seem to have any inter-dependency with the rest of the project, so as an implementation it is much better (than mine at least)
4

You could try something like the following:

import os
import os.path
def which(filename):
    """docstring for which"""
    locations = os.environ.get("PATH").split(os.pathsep)
    candidates = []
    for location in locations:
        candidate = os.path.join(location, filename)
        if os.path.isfile(candidate):
            candidates.append(candidate)
    return candidates

2 Comments

You need to take PATHEXT into account as well
On a Windows machine I suspect that you would likely look for the exact name of the file, as opposed to assuming the extensions. With that being said it wouldn't be hard to add an inner loop that iterates over the members of PATHEXT.
3

If you use shell=True, then your command will be run through the system shell, which will automatically find the binary on the path:

p = subprocess.Popen("abc", stdout=subprocess.PIPE, shell=True)

1 Comment

Even without shell=True it is looked up in path, but it does not help if you want to find which of the possible commands exists.
3

This is the equivalent of the which command, which not only checks if the file exists, but also whether it is executable:

import os

def which(file_name):
    for path in os.environ["PATH"].split(os.pathsep):
        full_path = os.path.join(path, file_name)
        if os.path.exists(full_path) and os.access(full_path, os.X_OK):
            return full_path
    return None

Comments

-2

Here's a one-line version of earlier answers:

import os
which = lambda y: next(filter(lambda x: os.path.isfile(x) and os.access(x,os.X_OK),[x+os.path.sep+y for x in os.getenv("PATH").split(os.pathsep)]),None)

used like so:

>>> which("ls")
'/bin/ls'

1 Comment

This doesn't seem to work in Python2, TypeError: list object is not an iterator.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.