90

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
Braiam
  • 1
  • 11
  • 47
  • 78
prosseek
  • 182,215
  • 215
  • 566
  • 871
  • Even in the shell itself, `which` itself is not a good choice for detecting if a command is installed. [Reference](http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script/677212#677212) – kojiro May 05 '15 at 18:26
  • Possible duplicate of [Test if executable exists in Python?](https://stackoverflow.com/questions/377017/test-if-executable-exists-in-python) – Bernhard Oct 10 '18 at 13:21

7 Answers7

96

There is distutils.spawn.find_executable().

zonksoft
  • 2,400
  • 1
  • 24
  • 36
  • 6
    +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) – orip Mar 28 '14 at 07:58
  • 2
    Beware it doesn't check whether file is executable. – temoto Mar 29 '16 at 00:46
  • 1
    This didn't work for me on 2.7 or 3.6. It gave an error that the spawn module wasn't found. – GreenMatt Oct 09 '18 at 19:19
  • @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. – jpmc26 Feb 11 '19 at 22:04
  • @GreenMatt, just tried in on 3.7, works just fine. As mentioned by others, don't forget to run `import distutils.spawn` first. – zonksoft Apr 18 '20 at 18:33
  • 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](https://github.com/python/cpython/blob/master/Lib/distutils/spawn.py#L101)). 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. – adzenith Jan 14 '21 at 17:12
94

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'
iLoveTux
  • 3,552
  • 23
  • 31
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!

Gonzalo Larralde
  • 3,523
  • 25
  • 30
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
John Percival Hackworth
  • 11,395
  • 2
  • 29
  • 38
  • You need to take `PATHEXT` into account as well – orip Mar 08 '11 at 00:47
  • 2
    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. – John Percival Hackworth Mar 08 '11 at 01:21
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
o9000
  • 1,626
  • 13
  • 15
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)
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
-1

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'