用Python在Windows下模仿Linux的|which.exe|程序

简介

用Python在Windows下模仿Linux的|which.exe|程序。

这个程序会从Windows环境变量|PATH|里列出的所有目录中查找"可执行文件"。

在Linux下|which.exe|通过文件的"可执行"属性来判断文件是否可执行不同。

在Windows下我们通过扩展名来判断。

如果一个文件的扩展名是|.exe|, |.bat|,或者任何在Windows环境变量|PATHEXT|中列出的扩展名(比如我们自己添加的|.py|),则认为这个文件为"可执行文件"。这个定义也符合Windows的CMD对"可执行文件"的定义。

源码

闲话少说,先上源码。实现的具体分析见源码中注释及源码后的分析。

__doc__ = """
Author: kuiyuyou@gmail.com
File: aoiwhich.py
"""

import sys
import os

def find_executable(program):
    """ Given |program| which is the name or path of a program, find a list of
    executable paths of the program.

    #// Arguments
    program: the name or path of a program. No need to include extension.
    return: a list of executable paths of the program.

    #// What an "executable" means?
    An executable means an existing file whose extension is |.exe|, |.bat|, or
    any other extensions listed in Windows environment variable |PATHEXT|.

    #// What is the algorithm used to find all the executable paths?
    Use each of dir paths listed in Windows environment variable |PATH| as parent dir.

    Use each of extensions listed in Windows environment variable |PATHEXT| as extension.

    Synthesize all the possible paths by combining each parent dir, the |program| given,
    and each extension. (Empty parent dir and empty extension are two special cases.)

    Check if each path synthesized is an existing executable path.
    """
    #//
    if not isinstance(program, str): raise TypeError(program)

    #// create a list to store all the executable paths found
    the_path_s = []

    #// get environment variable |PATHEXT|'s value
    the_PATHEXT = os.environ.get('PATHEXT')
    ## |os.environ.get| returns either a str or None

    #// split |the_PATHEXT| into a list of extensions
    if the_PATHEXT is None:
        the_ext_s = []
    else:
        the_ext_s = the_PATHEXT.split(os.pathsep)
        ## |os.pathsep| on Windows is |;|

    #// change each item in [the_ext_s] into lowercase
    the_ext_s = [x.lower() for x in the_ext_s]

    #// add '.exe' to |the_ext_s| if it is NOT in
    if '.exe' not in the_ext_s:
        the_ext_s.append('.exe')

    #// add '.bat' to |the_ext_s| if it is NOT in
    if '.bat' not in the_ext_s:
        the_ext_s.append('.bat')

    ## The reason to add '.exe' and '.bat':
    ##
    ## On Windows, files with an extension of '.exe' or '.bat' are considered
    ## executable, even if '.exe' and '.bat' are not listed in environment
    ## variable |PATHEXT|.
    ##
    ## However, the algorithm below requires that, an "executable" msut have one
    ## of the extensions in |the_ext_s|. So to ensure that the algorithm can
    ## always recognize '.exe' and '.bat' files as executable, we add '.exe' and
    ## '.bat' to |the_ext_s| manually if they are not listed in |PATHEXT|.

    #// get a tuple version of |the_ext_s|
    ## becasue |string.endswith| below takes tuple but not list as argument
    the_ext_s_tuple = tuple(the_ext_s)

    #// check if |program| given is an executable full path
    if os.path.isfile(program) and program.endswith(the_ext_s_tuple):
        the_path_s.append(program)
        ## do not return immediately here because if |program| is a relative path,
        ## algorithm below may find its absolute path as well, e.g.
        ## if |aoiwhich.py| file's parent dir has been added to PATH, then
        ## in the same dir, run command |aoiwhich.py aoiwhich.py| would find both
        ## the relative path |aoiwhich.py| as well as an absolute path.

    #// get the environment variable |PATH|'s value
    the_PATH = os.environ.get('PATH')
    ## |os.environ.get| returns either a str or None

    #// split |the_PATH| into a list of dir paths
    if the_PATH is None:
        the_dir_path_s = []
    else:
        the_dir_path_s = the_PATH.split(os.pathsep)
        ## |os.pathsep| on Windows is |;|

    #// insert empty dir path to the beginning of the list of dir paths
    if '' not in the_dir_path_s:
        the_dir_path_s.insert(0, '')

    #// get unique dir paths
    the_dir_path_s_unique = []
    for the_dir_path in the_dir_path_s:
        if the_dir_path not in the_dir_path_s_unique:
            the_dir_path_s_unique.append(the_dir_path)

    #// synthesize possible executable paths.
    #// check whether each of the synthesized executable paths is existing executable path.
    for the_dir_path in the_dir_path_s_unique:
        # synthesize a path
        # use |the_dir_path| as the parent dir
        # use |program| as the file name (assuming it has included extension)
        # use no extension 
        the_path_synthesized = os.path.join(the_dir_path, program)

        # check if the path synthesized is an existing executable path
        if os.path.isfile(the_path_synthesized) and the_path_synthesized.endswith(the_ext_s_tuple):
            the_path_s.append(the_path_synthesized)

        #
        for the_ext in the_ext_s:
            # synthesize a path
            # use |the_dir_path| as the parent dir
            # use |program| as the file name (assuming it has NOT included extension)
            # use |the_ext| as the extension
            the_path_synthesized_plus_ext = the_path_synthesized + the_ext

            # check if the path synthesized is an existing executable path
            if os.path.isfile(the_path_synthesized_plus_ext):
                the_path_s.append(the_path_synthesized_plus_ext)

    #// make the list items unique
    the_path_s_unique = []
    for the_path in the_path_s:
        if the_path not in the_path_s_unique:
            the_path_s_unique.append(the_path)

    #//
    return the_path_s_unique

if __name__ == '__main__':
    #//
    if len(sys.argv) != 2:
        print('Usage: {} PROGRAM'.format(sys.argv[0]))
        print('Example: {} {}'.format(sys.argv[0], 'notepad'))
        sys.exit(0)
    ## I was using |argparse| module to parse the arguments, which was very
    ## convenient. However, |argparse| module is available only since python
    ## version 3.2. So here I simply check the number of arguments given instead.

    #//
    the_program = sys.argv[1]

    #// find paths according to program name given
    the_path_s = find_executable(the_program)

    #// has found NO paths, exit
    if len(the_path_s) == 0:
        sys.exit(0)

    #// has found some paths, print
    print('\n'.join(the_path_s))

分析

通过|os.environ.get('PATHEXT')|和|os.environ.get('PATH')|分别得到Window环境变量|PATHEXT|和|PATH|的值。 ## 注意,当这些变量在注册表中没有定义时,|os.environ.get|返回的是None而不是空的list。

将这两个值用|split|拆分为多项。|PATHEXT|的每一个项是一个扩展名(包括点)。|PATH|的每一个项是一个目录路径。

通过将每一个目录路径,与函数参数|program|,与每一个扩展名相组合,得到所有可能的可执行文件的路径。 ## 有两个特殊情况应考虑在内,一个是目录为当前目录,一个是函数参数|program|已经包含了扩展名。

再用|os.path.isfile|判断该路径是否是一个存在的文件。

最后将找到的所有路径打印出来。

用法

先将存有源码的文件命名为|aoiwhich.py|。

在命令行运行:

python aoiwhich.py notepad

在俺的机器上得到:

C:\Windows\system32\notepad.exe
C:\Windows\notepad.exe

在命令行运行:

python aoiwhich.py regedit

在俺的机器上得到:

C:\Windows\system32\regedit.exe
C:\Windows\regedit.exe

此软件已开源到 http://sourceforge.net/p/aoiwhich/ 。

转载于:https://my.oschina.net/kuiyuyou/blog/52121

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值