I'm writing grepath
utility that finds executables in %PATH%
that match a pattern.
I need to define whether given filename in the path is executable (emphasis is on command line scripts).
Based on "Tell if a file is executable" I've got:
import os
from pywintypes import error
from win32api import FindExecutable, GetLongPathName
def is_executable_win(path):
try:
_, executable = FindExecutable(path)
ext = lambda p: os.path.splitext(p)[1].lower()
if (ext(path) == ext(executable) # reject *.cmd~, *.bat~ cases
and samefile(GetLongPathName(executable), path)):
return True
# path is a document with assoc. check whether it has extension
# from %PATHEXT%
pathexts = os.environ.get('PATHEXT', '').split(os.pathsep)
return any(ext(path) == e.lower() for e in pathexts)
except error:
return None # not an exe or a document with assoc.
Where samefile
is:
try: samefile = os.path.samefile
except AttributeError:
def samefile(path1, path2):
rp = lambda p: os.path.realpath(os.path.normcase(p))
return rp(path1) == rp(path2)
How is_executable_win
could be improved in the given context? What functions from Win32 API could help?
P.S.
- time performance doesn't matter
subst
drives and UNC, unicode paths are not under consideration- C++ answer is OK if it uses functions available on Windows XP
Examples
notepad.exe
is executable (as a rule)which.py
is executable if it is associated with some executable (e.g., python.exe) and.PY
is in%PATHEXT%
i.e.,'C:\> which'
could start:some\path\python.exe another\path\in\PATH\which.py
somefile.doc
most probably is not executable (when it is associated with Word for example)another_file.txt
is not executable (as a rule)ack.pl
is executable if it is associated with some executable (most probably perl.exe) and.PL
is in%PATHEXT%
(i.e. I can runack
without specifing extension if it is in the path)
What is "executable" in this question
def is_executable_win_destructive(path):
#NOTE: it assumes `path` <-> `barename` for the sake of example
barename = os.path.splitext(os.path.basename(path))[0]
p = Popen(barename, stdout=PIPE, stderr=PIPE, shell=True)
stdout, stderr = p.communicate()
return p.poll() != 1 or stdout != '' or stderr != error_message(barename)
Where error_message()
depends on language. English version is:
def error_message(barename):
return "'%(barename)s' is not recognized as an internal" \
" or external\r\ncommand, operable program or batch file.\r\n" \
% dict(barename=barename)
If is_executable_win_destructive()
returns when it defines whether the path points to an executable for the purpose of this question.
Example:
>>> path = r"c:\docs\somefile.doc"
>>> barename = "somefile"
After that it executes %COMSPEC% (cmd.exe by default):
c:\cwd> cmd.exe /c somefile
If output looks like this:
'somefile' is not recognized as an internal or external command, operable program or batch file.
Then the path
is not an executable else it is (lets assume there is one-to-one correspondence between path
and barename
for the sake of example).
Another example:
>>> path = r'c:\bin\grepath.py'
>>> barename = 'grepath'
If .PY
in %PATHEXT%
and c:\bin
is in %PATH%
then:
c:\docs> grepath
Usage:
grepath.py [options] PATTERN
grepath.py [options] -e PATTERN
grepath.py: error: incorrect number of arguments
The above output is not equal to error_message(barename)
therefore 'c:\bin\grepath.py'
is an "executable".
So the question is how to find out whether the path
will produce the error without actually running it? What Win32 API function and what conditions used to trigger the 'is not recognized as an internal..' error?