Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tell whether a file is executable on Windows in Python?

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 run ack 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?

like image 523
jfs Avatar asked Mar 14 '09 23:03

jfs


1 Answers

shoosh beat me to it :)

If I remember correctly, you should try to read the first 2 characters in the file. If you get back "MZ", you have an exe.


hnd = open(file,"rb")
if hnd.read(2) == "MZ":
  print "exe"
like image 124
Geo Avatar answered Oct 18 '22 08:10

Geo