Welcome, guest | Sign In | My Account | Store | Cart

Given search paths separated by a separator, find the first file that matches a given specification.

Python, 25 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from os.path import exists, join
from os import pathsep
from string import split

def search_file(filename, search_path):
   """Given a search path, find file
   """
   file_found = 0
   paths = string.split(search_path, pathsep)
   for path in paths:
      if exists(join(path, filename)):
          file_found = 1
          break
   if file_found:
      return abspath(join(path, filename))
   else:
      return None

if __name__ == '___main__':
   search_path = '/bin' + pathsep + '/usr/bin'  # ; on windows, : on unix
   find_file = search_file('ls',search_path)
   if find_file:
      print "File found at %s" % find_file
   else:
      print "File not found"

9 comments

Mitch Chapman 23 years ago  # | flag

Variation: Finding files on the Python path. Larger Python applications consist of sets of Python packages and associated sets of "resource" files (e.g. Glade project files, SQL templates, image files). It's convenient to store these associated files together with the Python packages which use them. It's easy to do so if you use a variation on this recipe, one which finds files relative to the Python path.

#!/usr/bin/env python
import sys, os

class Error(Exception): pass

def _find(path, matchFunc=os.path.isfile):
    for dirname in sys.path:
        candidate = os.path.join(dirname, path)
        if matchFunc(candidate):
            return candidate
    raise Error("Can't find file %s" % path)

def find(path):
    return _find(path)

def findDir(path):
    return _find(path, matchFunc=os.path.isdir)

Mitch Chapman

Phil Rittenhouse 20 years, 2 months ago  # | flag

Small correction. The call to string.split(), in the search_file() function, should just be a call to split() since the example uses 'from string import split'.

Phil Rittenhouse 20 years, 2 months ago  # | flag

Another one. The abspath function is not imported. The first import statement should probably be 'from os.path import exists, join, abspath'.

Micah Elliott 18 years, 4 months ago  # | flag

Sometimes there are implicit extension on sought files. Here's a version that accommodates for implicit extensions (nice formatting system!)::

def findFile(seekName, path, implicitExt=''):
    """Given a pathsep-delimited path string, find seekName.
    Returns path to seekName if found, otherwise None.
    Also allows for files with implicit extensions (eg, .exe), but
    always returning seekName as was provided.
    >>> findFile('ls', '/usr/bin:/bin', implicitExt='.exe')
    '/bin/ls'
    """
    if (os.path.isfile(seekName)
          or implicitExt and os.path.isfile(seekName + implicitExt)):
        # Already absolute path.
        return seekName
    for p in path.split(os.pathsep):
        candidate = os.path.join(p, seekName)
        if (os.path.isfile(candidate)
              or implicitExt and os.path.isfile(candidate + implicitExt)):
            return candidate
    return None
Alejandro Decchi 16 years, 9 months ago  # | flag

Hello. Hi! I try to use this search code :

!/usr/bin/python

from os.path import exists, join from os import pathsep from string import split

def search_file(filename, search_path): """Given a search path, find file """ file_found = 0 paths = string.split(search_path, pathsep) for path in paths: if exists(join(path, filename)): file_found = 1 break if file_found: return abspath(join(path, filename)) else: return None

if __name__ == '___main__': search_path = '/bin' + pathsep + '/usr/bin' # ; on windows, : on unix find_file = search_file('ls',search_path) if find_file: print "File found at %s" % find_file else: print "File not found

But where i must put the word to search ???? and How can i do to download the file found ???

Donovan Baarda 13 years, 7 months ago  # | flag

The example provided are rather verbose. This is much simpler;

def SearchPath(name, path=None, exts=('',)):
  """Search PATH for a binary.

  Args:
    name: the filename to search for
    path: the optional path string (default: os.environ['PATH')
    exts: optional list/tuple of extensions to try (default: ('',))

  Returns:
    The abspath to the binary or None if not found.
  """
  path = path or os.environ('PATH')
  for dir in path.split(os.pathsep):
    for ext in exts:
      binpath = os.path.join(dir, name) + ext
      if os.path.exists(binpath):
        return os.abspath(binpath)
  return None
Mike Koss 13 years, 6 months ago  # | flag

@Donovan - you had a couple of typo's in your code. Here's a simple version that is tested (I didn't need the extensions in my case, so I removed that):

def find_in_path(file_name): path = os.environ['PATH'] for d in path.split(os.pathsep): file_path = os.path.abspath(os.path.join(d, file_name)) if os.path.exists(file_path): return file_path return file_name

Dev Player 12 years, 10 months ago  # | flag

To point out one of those typos mentioned: os.environ('PATH') os.environ is a dict(), not a function. Calling it with parens () will raise an exception.

Also, because os.environ is a dict() type, os.environ['PATH']" will only work if the key which equals 'PATH' and the operating system's environment variable Path have the same case, that is upper() in this case. My MS Windows XP path environment varible did not work with the above examples because my OS returns "Path" as that key in os.environ and not "PATH".

It's likely more robust to use S = os.getenv('PATH'). And then use L = S.split(os.pathsep) or combine them into path = os.getenv('PATH').split(os.pathsep). os.getenv() to account for case issues.

If you might use this file search routing in some os.spawn, subprocess or multiprocess calls (as searching routines like this can really "block" [look it up]) consider two things.

One, there is a security risk if you do os.access() with a context manager (ie with file.open() as f:). See docs. This might be the case where you use this file search routine with some other file manipulation routine, like during an os.path.walk() or sum such thing.

And two, if you run spawn or subprocess or multiprocess and use options that do not carry over the environment varibles into the new process there will not be a PATH operating system environment varible at all. And you won't find your file and likely raise an exception.

To add a little more protection in these cases use os.getenv()'s second parameter, the "default" value; like this: os.getenv('PATH', os.defpath).

If you might use this filename search routine in an iterator, generator or os.path.walk()-like function you might want to include the current and parent paths in the beginning of the search; like this: os.curdir + os.pathsep + os.pardir + os.pathsep

Putting that logic together into a neat little function you get:

import os
def GetOSPath():
    immediate = os.curdir + os.pathsep + os.pardir + os.pathsep
    ospath = os.getenv('PATH', os.defpath))
    path = immediate + ospath
    return path.split(os.pathsep)
Dev Player 12 years, 10 months ago  # | flag

I believe you may need to modify either a line in the function:

os.path.join(dir, name) + ext

if dir == r"C:\bin" and

if name == 'myprog' and

if ext == 'exe' and

if full == os.path.join(dir, name) + ext

then print full does

r"C:\bin\myprogexe"

and not r"C:\bin\myprog.exe"

So you could do:

full = os.path.join(dir, name) + os.extsep + ext

Or you may need to modify the format expected of the exts function argument.

Consider:

exts = ('', 'exe', 'com', 'bat')
exts_n = [os.extsep + ext for ext in exts if ext is not '']
if '' in exts: exts_n.append('')
exts = exts_n

So that you get "myprog.exe" and not "myprogexe"