C++ 如何在Python中判断文件是否在Windows上可执行?

C++ 如何在Python中判断文件是否在Windows上可执行?,c++,python,windows,winapi,C++,Python,Windows,Winapi,我正在编写一个实用程序,用于在%PATH%中查找与模式匹配的可执行文件。 我需要定义路径中给定的文件名是否可执行(重点是命令行脚本) 根据我得到的: import os from pywintypes import error from win32api import FindExecutable, GetLongPathName def is_executable_win(path): try: _, executable = FindExecutable(pat

我正在编写一个实用程序,用于在
%PATH%
中查找与模式匹配的可执行文件。 我需要定义路径中给定的文件名是否可执行(重点是命令行脚本)

根据我得到的:

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.
其中
samefile
是:

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)
在给定的上下文中,如何改进
是可执行的\u win
?Win32 API中的哪些函数可以提供帮助

附言

  • 时间表现并不重要
  • subst
    驱动器和UNC、unicode路径不在考虑之列
  • 如果C++使用Windows XP上可用的函数,则答案是确定的
例子
  • notepad.exe
    是可执行的(通常)
  • 哪个.py
    是可执行的,如果它与某些可执行文件(例如python.exe)关联,并且
    .py
    位于
    %PATHEXT%
    中,即
    'C:\>哪个“
    可以启动:

    some\path\python.exe another\path\in\PATH\which.py
    
  • somefile.doc
    很可能是不可执行的(例如,当它与Word关联时)

  • 另一个_file.txt
    不可执行(通常)
  • ack.pl
    是可执行的,如果它与某些可执行文件(很可能是perl.exe)关联,并且
    .pl
    位于
    %PATHEXT%
    (即,如果它位于路径中,我可以运行
    ack
    ,而不指定扩展名)
这个问题中的“可执行文件”是什么 如果
is\u executable\u win\u destromical()
在定义路径是否指向用于此问题的可执行文件时返回

例如:

>>> path = r"c:\docs\somefile.doc"
>>> barename = "somefile"
然后执行%COMSPEC%(默认为cmd.exe):

如果输出如下所示:

'somefile' is not recognized as an internal or external command, operable program or batch file. 如果
%PATH%
中的
.PY
c:\bin
位于
%PATH%
中,则:

c:\docs> grepath
Usage:
  grepath.py [options] PATTERN
  grepath.py [options] -e PATTERN

grepath.py: error: incorrect number of arguments
上述输出不等于
错误消息(barename)
,因此
'c:\bin\grepath.py'
是一个“可执行文件”

因此,问题是如何找出
路径
是否会在不实际运行的情况下产生错误?用于触发“未识别为内部..”错误的Win32 API函数和条件是什么?

windows始终以字符“MZ”开头。但是,这也包括不一定是可执行文件的任何类型的DLL。
然而,要检查这一点,您必须打开文件并读取标题,因此这可能不是您要查找的内容。

shoosh抢先一步:)

如果我没记错的话,您应该尝试读取文件中的前2个字符。如果你回到“MZ”,你有一个exe


hnd = open(file,"rb")
if hnd.read(2) == "MZ":
  print "exe"
解析PE格式

除了使用python来实际运行程序之外,这可能是最好的解决方案

编辑:我知道您还需要具有关联的文件。这将需要在注册表中捣乱,我没有相关信息


Edit2:我还看到了.doc和.py之间的区别。这是一个相当随意的区别,必须用手动规则指定,因为对于windows来说,它们都是程序读取的文件扩展名。

您的问题无法回答。Windows无法区分与脚本语言关联的文件与其他任意程序之间的区别。就Windows而言,.PY文件只是一个由python.exe打开的文档。

我认为,这就足够了:

  • 检查PATHEXT中的文件扩展名-文件是否可直接执行
  • 使用cmd.exe命令“assoc.ext”可以查看文件是否与某些可执行文件关联(启动此文件时将启动某些可执行文件)。您可以不带参数地解析assoc的捕获输出,收集所有关联的扩展名,并检查测试的文件扩展名
  • 其他文件扩展名将触发错误“无法识别命令…”,因此您可以假定这些文件不可执行
  • 我真的不明白如何区分somefile.py和somefile.txt之间的区别,因为关联实际上是相同的。您可以将系统配置为以与.py文件相同的方式运行.txt文件。

    以下是我在问题中链接的:

    #!/usr/bin/env python
    """Find executables in %PATH% that match PATTERN.
    
    """
    #XXX: remove --use-pathext option
    
    import fnmatch, itertools, os, re, sys, warnings
    from optparse import OptionParser
    from stat import S_IMODE, S_ISREG, ST_MODE
    from subprocess import PIPE, Popen
    
    
    def warn_import(*args):
        """pass '-Wd' option to python interpreter to see these warnings."""
        warnings.warn("%r" % (args,), ImportWarning, stacklevel=2)
    
    
    class samefile_win:
        """
    http://timgolden.me.uk/python/win32_how_do_i/see_if_two_files_are_the_same_file.html
    """
        @staticmethod
        def get_read_handle (filename):
            return win32file.CreateFile (
                filename,
                win32file.GENERIC_READ,
                win32file.FILE_SHARE_READ,
                None,
                win32file.OPEN_EXISTING,
                0,
                None
                )
    
        @staticmethod
        def get_unique_id (hFile):
            (attributes,
             created_at, accessed_at, written_at,
             volume,
             file_hi, file_lo,
             n_links,
             index_hi, index_lo
             ) = win32file.GetFileInformationByHandle (hFile)
            return volume, index_hi, index_lo
    
        @staticmethod
        def samefile_win(filename1, filename2):
            """Whether filename1 and filename2 represent the same file.
    
    It works for subst, ntfs hardlinks, junction points.
    It works unreliably for network drives.
    
    Based on GetFileInformationByHandle() Win32 API call.
    http://timgolden.me.uk/python/win32_how_do_i/see_if_two_files_are_the_same_file.html
    """
            if samefile_generic(filename1, filename2): return True
            try:
                hFile1 = samefile_win.get_read_handle (filename1)
                hFile2 = samefile_win.get_read_handle (filename2)
                are_equal = (samefile_win.get_unique_id (hFile1)
                             == samefile_win.get_unique_id (hFile2))
                hFile2.Close ()
                hFile1.Close ()
                return are_equal
            except win32file.error:
                return None
    
    
    def canonical_path(path):
        """NOTE: it might return wrong path for paths with symbolic links."""
        return os.path.realpath(os.path.normcase(path))
    
    
    def samefile_generic(path1, path2):
        return canonical_path(path1) == canonical_path(path2)
    
    
    class is_executable_destructive:
        @staticmethod
        def error_message(barename):
            r"""
    "'%(barename)s' is not recognized as an internal or external\r\n
    command, operable program or batch file.\r\n"
    
    in Russian:
    """
            return '"%(barename)s" \xad\xa5 \xef\xa2\xab\xef\xa5\xe2\xe1\xef \xa2\xad\xe3\xe2\xe0\xa5\xad\xad\xa5\xa9 \xa8\xab\xa8 \xa2\xad\xa5\xe8\xad\xa5\xa9\r\n\xaa\xae\xac\xa0\xad\xa4\xae\xa9, \xa8\xe1\xaf\xae\xab\xad\xef\xa5\xac\xae\xa9 \xaf\xe0\xae\xa3\xe0\xa0\xac\xac\xae\xa9 \xa8\xab\xa8 \xaf\xa0\xaa\xa5\xe2\xad\xeb\xac \xe4\xa0\xa9\xab\xae\xac.\r\n' % dict(barename=barename)
    
        @staticmethod
        def is_executable_win_destructive(path):
            # assume path <-> barename that is false in general
            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)
    
    
    def is_executable_win(path):
        """Based on:
    http://timgolden.me.uk/python/win32_how_do_i/tell-if-a-file-is-executable.html
    
    Known bugs: treat some "*~" files as executable, e.g. some "*.bat~" files
    """
        try:
            _, executable = FindExecutable(path)
            return bool(samefile(GetLongPathName(executable), path))
        except error:
            return None # not an exe or a document with assoc.
    
    
    def is_executable_posix(path):
        """Whether the file is executable.
    
    Based on which.py from stdlib
    """
        #XXX it ignores effective uid, guid?
        try: st = os.stat(path)
        except os.error:
            return None
    
        isregfile = S_ISREG(st[ST_MODE])
        isexemode = (S_IMODE(st[ST_MODE]) & 0111)
        return bool(isregfile and isexemode)
    
    try:
        #XXX replace with ctypes?
        from win32api import FindExecutable, GetLongPathName, error
        is_executable = is_executable_win
    except ImportError, e:
        warn_import("is_executable: fall back on posix variant", e)
        is_executable = is_executable_posix
    
    try: samefile = os.path.samefile
    except AttributeError, e:
        warn_import("samefile: fallback to samefile_win", e)
        try:
            import win32file
            samefile = samefile_win.samefile_win
        except ImportError, e:
            warn_import("samefile: fallback to generic", e)
            samefile = samefile_generic
    
    def main():
        parser = OptionParser(usage="""
    %prog [options] PATTERN
    %prog [options] -e PATTERN""", description=__doc__)
        opt = parser.add_option
        opt("-e", "--regex", metavar="PATTERN",
            help="use PATTERN as a regular expression")
        opt("--ignore-case", action="store_true", default=True,
            help="""[default] ignore case when --regex is present; for \
    non-regex PATTERN both FILENAME and PATTERN are first \
    case-normalized if the operating system requires it otherwise \
    unchanged.""")
        opt("--no-ignore-case", dest="ignore_case", action="store_false")
        opt("--use-pathext", action="store_true", default=True,
            help="[default] whether to use %PATHEXT% environment variable")
        opt("--no-use-pathext", dest="use_pathext", action="store_false")
        opt("--show-non-executable", action="store_true", default=False,
            help="show non executable files")
    
        (options, args) = parser.parse_args()
    
        if len(args) != 1 and not options.regex:
           parser.error("incorrect number of arguments")
        if not options.regex:
           pattern = args[0]
        del args
    
        if options.regex:
           filepred = re.compile(options.regex, options.ignore_case and re.I).search
        else:
           fnmatch_ = fnmatch.fnmatch if options.ignore_case else fnmatch.fnmatchcase
           for file_pattern_symbol in "*?":
               if file_pattern_symbol in pattern:
                   break
           else: # match in any place if no explicit file pattern symbols supplied
               pattern = "*" + pattern + "*"
           filepred = lambda fn: fnmatch_(fn, pattern)
    
        if not options.regex and options.ignore_case:
           filter_files = lambda files: fnmatch.filter(files, pattern)
        else:
           filter_files = lambda files: itertools.ifilter(filepred, files)
    
        if options.use_pathext:
           pathexts = frozenset(map(str.upper,
                os.environ.get('PATHEXT', '').split(os.pathsep)))
    
        seen = set()
        for dirpath in os.environ.get('PATH', '').split(os.pathsep):
            if os.path.isdir(dirpath): # assume no expansion needed
               # visit "each" directory only once
               # it is unaware of subst drives, junction points, symlinks, etc
               rp = canonical_path(dirpath)
               if rp in seen: continue
               seen.add(rp); del rp
    
               for filename in filter_files(os.listdir(dirpath)):
                   path = os.path.join(dirpath, filename)
                   isexe = is_executable(path)
    
                   if isexe == False and is_executable == is_executable_win:
                      # path is a document with associated program
                      # check whether it is a script (.pl, .rb, .py, etc)
                      if not isexe and options.use_pathext:
                         ext = os.path.splitext(path)[1]
                         isexe = ext.upper() in pathexts
    
                   if isexe:
                      print path
                   elif options.show_non_executable:
                      print "non-executable:", path
    
    
    if __name__=="__main__":
       main()
    
    #/usr/bin/env python
    “”“在%PATH%中查找与模式匹配的可执行文件。”。
    """
    #XXX:删除--使用pathext选项
    导入fnmatch、itertools、os、re、sys、警告
    从optpasse导入OptionParser
    从统计导入S_IMODE、S_ISREG、ST_模式
    从子流程导入管道,Popen
    def warn_导入(*args):
    “”“将'-Wd'选项传递给python解释器以查看这些警告。”“”
    警告。警告(“%r”%”(参数,),导入警告,堆栈级别=2)
    类samefile_win:
    """
    http://timgolden.me.uk/python/win32_how_do_i/see_if_two_files_are_the_same_file.html
    """
    @静力学方法
    def get_read_句柄(文件名):
    返回win32file.CreateFile(
    文件名,
    win32file.GENERIC\u读取,
    win32file.FILE\u共享\u读取,
    没有一个
    win32file.OPEN_存在,
    0,
    没有一个
    )
    @静力学方法
    def get_unique_id(hFile):
    (b)属性,
    创建时,访问时,写入时,
    卷,
    文件你好,文件你好,
    n_链接,
    索引高,索引低
    )=win32file.GetFileInformationByHandle(hFile)
    返回量,索引高,索引低
    @静力学方法
    def samefile_win(文件名1、文件名2):
    “”“文件名1和文件名2是否代表同一个文件。”。
    它适用于subst、ntfs硬链接和连接点。
    它对网络驱动器的工作不可靠。
    基于GetFileInformationByHandle()Win32 API调用。
    http://timgolden.me.uk/python/win32_how_do_i/see_if_two_files_are_the_same_file.html
    """
    如果samefile_generic(filename1,filename2):返回True
    尝试:
    hFile1=samefile\u win.get\u read\u句柄(filename1)
    hFile2=samefile\u win.get\u read\u句柄(filename2)
    是否相等=(samefile\u win.get\u unique\u id(hFil
    
    >>> path = r'c:\bin\grepath.py'
    >>> barename = 'grepath'
    
    c:\docs> grepath
    Usage:
      grepath.py [options] PATTERN
      grepath.py [options] -e PATTERN
    
    grepath.py: error: incorrect number of arguments
    
    
    hnd = open(file,"rb")
    if hnd.read(2) == "MZ":
      print "exe"
    
    #!/usr/bin/env python
    """Find executables in %PATH% that match PATTERN.
    
    """
    #XXX: remove --use-pathext option
    
    import fnmatch, itertools, os, re, sys, warnings
    from optparse import OptionParser
    from stat import S_IMODE, S_ISREG, ST_MODE
    from subprocess import PIPE, Popen
    
    
    def warn_import(*args):
        """pass '-Wd' option to python interpreter to see these warnings."""
        warnings.warn("%r" % (args,), ImportWarning, stacklevel=2)
    
    
    class samefile_win:
        """
    http://timgolden.me.uk/python/win32_how_do_i/see_if_two_files_are_the_same_file.html
    """
        @staticmethod
        def get_read_handle (filename):
            return win32file.CreateFile (
                filename,
                win32file.GENERIC_READ,
                win32file.FILE_SHARE_READ,
                None,
                win32file.OPEN_EXISTING,
                0,
                None
                )
    
        @staticmethod
        def get_unique_id (hFile):
            (attributes,
             created_at, accessed_at, written_at,
             volume,
             file_hi, file_lo,
             n_links,
             index_hi, index_lo
             ) = win32file.GetFileInformationByHandle (hFile)
            return volume, index_hi, index_lo
    
        @staticmethod
        def samefile_win(filename1, filename2):
            """Whether filename1 and filename2 represent the same file.
    
    It works for subst, ntfs hardlinks, junction points.
    It works unreliably for network drives.
    
    Based on GetFileInformationByHandle() Win32 API call.
    http://timgolden.me.uk/python/win32_how_do_i/see_if_two_files_are_the_same_file.html
    """
            if samefile_generic(filename1, filename2): return True
            try:
                hFile1 = samefile_win.get_read_handle (filename1)
                hFile2 = samefile_win.get_read_handle (filename2)
                are_equal = (samefile_win.get_unique_id (hFile1)
                             == samefile_win.get_unique_id (hFile2))
                hFile2.Close ()
                hFile1.Close ()
                return are_equal
            except win32file.error:
                return None
    
    
    def canonical_path(path):
        """NOTE: it might return wrong path for paths with symbolic links."""
        return os.path.realpath(os.path.normcase(path))
    
    
    def samefile_generic(path1, path2):
        return canonical_path(path1) == canonical_path(path2)
    
    
    class is_executable_destructive:
        @staticmethod
        def error_message(barename):
            r"""
    "'%(barename)s' is not recognized as an internal or external\r\n
    command, operable program or batch file.\r\n"
    
    in Russian:
    """
            return '"%(barename)s" \xad\xa5 \xef\xa2\xab\xef\xa5\xe2\xe1\xef \xa2\xad\xe3\xe2\xe0\xa5\xad\xad\xa5\xa9 \xa8\xab\xa8 \xa2\xad\xa5\xe8\xad\xa5\xa9\r\n\xaa\xae\xac\xa0\xad\xa4\xae\xa9, \xa8\xe1\xaf\xae\xab\xad\xef\xa5\xac\xae\xa9 \xaf\xe0\xae\xa3\xe0\xa0\xac\xac\xae\xa9 \xa8\xab\xa8 \xaf\xa0\xaa\xa5\xe2\xad\xeb\xac \xe4\xa0\xa9\xab\xae\xac.\r\n' % dict(barename=barename)
    
        @staticmethod
        def is_executable_win_destructive(path):
            # assume path <-> barename that is false in general
            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)
    
    
    def is_executable_win(path):
        """Based on:
    http://timgolden.me.uk/python/win32_how_do_i/tell-if-a-file-is-executable.html
    
    Known bugs: treat some "*~" files as executable, e.g. some "*.bat~" files
    """
        try:
            _, executable = FindExecutable(path)
            return bool(samefile(GetLongPathName(executable), path))
        except error:
            return None # not an exe or a document with assoc.
    
    
    def is_executable_posix(path):
        """Whether the file is executable.
    
    Based on which.py from stdlib
    """
        #XXX it ignores effective uid, guid?
        try: st = os.stat(path)
        except os.error:
            return None
    
        isregfile = S_ISREG(st[ST_MODE])
        isexemode = (S_IMODE(st[ST_MODE]) & 0111)
        return bool(isregfile and isexemode)
    
    try:
        #XXX replace with ctypes?
        from win32api import FindExecutable, GetLongPathName, error
        is_executable = is_executable_win
    except ImportError, e:
        warn_import("is_executable: fall back on posix variant", e)
        is_executable = is_executable_posix
    
    try: samefile = os.path.samefile
    except AttributeError, e:
        warn_import("samefile: fallback to samefile_win", e)
        try:
            import win32file
            samefile = samefile_win.samefile_win
        except ImportError, e:
            warn_import("samefile: fallback to generic", e)
            samefile = samefile_generic
    
    def main():
        parser = OptionParser(usage="""
    %prog [options] PATTERN
    %prog [options] -e PATTERN""", description=__doc__)
        opt = parser.add_option
        opt("-e", "--regex", metavar="PATTERN",
            help="use PATTERN as a regular expression")
        opt("--ignore-case", action="store_true", default=True,
            help="""[default] ignore case when --regex is present; for \
    non-regex PATTERN both FILENAME and PATTERN are first \
    case-normalized if the operating system requires it otherwise \
    unchanged.""")
        opt("--no-ignore-case", dest="ignore_case", action="store_false")
        opt("--use-pathext", action="store_true", default=True,
            help="[default] whether to use %PATHEXT% environment variable")
        opt("--no-use-pathext", dest="use_pathext", action="store_false")
        opt("--show-non-executable", action="store_true", default=False,
            help="show non executable files")
    
        (options, args) = parser.parse_args()
    
        if len(args) != 1 and not options.regex:
           parser.error("incorrect number of arguments")
        if not options.regex:
           pattern = args[0]
        del args
    
        if options.regex:
           filepred = re.compile(options.regex, options.ignore_case and re.I).search
        else:
           fnmatch_ = fnmatch.fnmatch if options.ignore_case else fnmatch.fnmatchcase
           for file_pattern_symbol in "*?":
               if file_pattern_symbol in pattern:
                   break
           else: # match in any place if no explicit file pattern symbols supplied
               pattern = "*" + pattern + "*"
           filepred = lambda fn: fnmatch_(fn, pattern)
    
        if not options.regex and options.ignore_case:
           filter_files = lambda files: fnmatch.filter(files, pattern)
        else:
           filter_files = lambda files: itertools.ifilter(filepred, files)
    
        if options.use_pathext:
           pathexts = frozenset(map(str.upper,
                os.environ.get('PATHEXT', '').split(os.pathsep)))
    
        seen = set()
        for dirpath in os.environ.get('PATH', '').split(os.pathsep):
            if os.path.isdir(dirpath): # assume no expansion needed
               # visit "each" directory only once
               # it is unaware of subst drives, junction points, symlinks, etc
               rp = canonical_path(dirpath)
               if rp in seen: continue
               seen.add(rp); del rp
    
               for filename in filter_files(os.listdir(dirpath)):
                   path = os.path.join(dirpath, filename)
                   isexe = is_executable(path)
    
                   if isexe == False and is_executable == is_executable_win:
                      # path is a document with associated program
                      # check whether it is a script (.pl, .rb, .py, etc)
                      if not isexe and options.use_pathext:
                         ext = os.path.splitext(path)[1]
                         isexe = ext.upper() in pathexts
    
                   if isexe:
                      print path
                   elif options.show_non_executable:
                      print "non-executable:", path
    
    
    if __name__=="__main__":
       main()