【问题标题】:os.system to invoke an exe which lies in a dir whose name contains whitespaceos.system 调用位于名称包含空格的目录中的 exe
【发布时间】:2011-10-22 01:50:04
【问题描述】:

我的代码简单如下:

file = 'C:\\Exe\\First Version\\filename.exe'
os.system(file)

当我运行这个程序时,出现了一个 Windows 错误:can't find the file specified.

我发现问题出在“第一版”中间的空白处。我怎样才能找到一种方法来规避这个问题?

P.S.:如果变量“file”作为参数传递给另一个函数怎么办?

【问题讨论】:

标签: python windowserror


【解决方案1】:

在路径周围加上引号会起作用:

file = 'C:\\Exe\\First Version\\filename.exe'
os.system('"' + file + '"')

但更好的解决方案是改用subprocess 模块:

import subprocess
file = 'C:\\Exe\\First Version\\filename.exe'
subprocess.call([file])

【讨论】:

  • 谢谢,它有效。我知道子进程会更好,但是我想从脚本内部调用的文件实际上不是 .exe 文件,而是某些文件以 .qgi 之类的后缀结尾,而这种 .qgi 文件不能简单地由子进程调用,因为我尝试过,但出现错误。
  • @Synapse -- 尝试使用p=subprocess.popen(file, shell=True),然后使用p.wait()
  • 这对我不起作用 在路径周围加上引号会起作用:cmd = 'C:\\Program Files\\Tesseract-OCR\\tesseract.exe'
【解决方案2】:

我用过这个:

import subprocess, shlex
mycmd='"C:\\Program Files\\7-Zip\\7z" x "D:\\my archive.7z" -o"D:\\extract folder" -aou'
subprocess.run(shlex.split(mycmd))

【讨论】:

  • 你太棒了!我花了整整一小时尝试了 10 种其他可能性,而这个解决了它!我欠你一个!
【解决方案3】:

尝试用双引号括起来。

file = '"C:\\Exe\\First Version\\filename.exe"'
os.system(file)

【讨论】:

    【解决方案4】:

    os.system 确实会启动一个二进制文件,该二进制文件在路径中包含空格,方法是将该路径包含在引号中。 (如果您习惯于使用终端,这应该是一个非常明显的解决方案。)然而,它本身并不能解决这个功能更痛苦的问题......一旦你这样做了,你就可以运行为您的命令添加参数时遇到麻烦! (啊!)

    所有当前的建议是现在使用subprocess 模块,而不是这个旧的、不受欢迎的功能。也可以使用shlx 将平面字符串转换为那些子进程函数的列表。我也遇到了这些方法的问题或痛苦,我不会漫不经心地谈论......而且,有时使用os.system 会更容易,因为你想要的只是外壳上的薄包装,隐式显示输出在控制台上流式传输,同步工作等。我当然希望有一个内置函数可以像这样在 shell 上执行命令,绝对 0 解析、包装、抽象... p>

    由于没有没有“过滤器”的内置,这是我的os.system 解决方案补丁。这是从我的一个开源库中提取的。这已经在 Windows、Mac 和 Ubuntu Linux 上进行了测试。我知道这不是 100% 万无一失的,而且它比人们想的要复杂得多,但也不算太糟糕。

    当您调用此 _system() 包装器(传递要执行的字符串)时,只需将您的长路径用引号括起来,并包括任何需要或不带引号的参数(即与您在终端中输入命令完全相同) !)。在命令中的第一个“令牌”上,这将删除 Mac 或 Linux 路径中的引号和转义空格。在 Windows 上,它通过实际解析给定环境中的内容来使用“短名称”。这部分代码有点棘手。基本上,它使用批处理机制进行名称解析,并将结果通过 stderr 发送回,目的是解析您在 stdout 上获得的 Popen() 结果。

    我想我包含了您需要的所有导入和定义。如果我遗漏了任何内容(复制和粘贴源代码尖峰),请告诉我。

    from os import system, getcwd, chdir
    from subprocess import Popen, PIPE
    
    import platform
    __plat = platform.system()
    IS_WINDOWS = __plat == "Windows"
    IS_LINUX   = __plat == "Linux"
    IS_MACOS   = __plat == "Darwin"
    
    __SCRUB_CMD_TMPL = "{0}{1}"
    __DBL_QUOTE      = '"'
    __SPACE          = ' '
    __ESC_SPACE      = '\\ '
    if IS_WINDOWS :        
        __BATCH_RUN_AND_RETURN_CMD = ["cmd","/K"] # simply assuming cmd is on the system path... 
        __BATCH_ONE_LINER_TMPLT    = "{0} 1>&2\n" # the newline triggers execution when piped in via stdin
        __BATCH_ESCAPE_PATH_TMPLT  = 'for %A in ("{0}") do @echo %~sA' 
        from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW
        __BATCH_ONE_LINER_STARTUPINFO = STARTUPINFO()
        __BATCH_ONE_LINER_STARTUPINFO.dwFlags |= STARTF_USESHOWWINDOW 
    
    def _system( cmd, wrkDir=None ):
        if wrkDir is not None:
            initWrkDir = getcwd()
            print( 'cd "%s"' % (wrkDir,) )
            chdir( wrkDir  )
        cmd = __scrubSystemCmd( cmd )        
        print( cmd )
        system( cmd ) 
        print('')
        if wrkDir is not None: chdir( initWrkDir )
    
    def __scrubSystemCmd( cmd ):
        """
        os.system is more convenient than the newer subprocess functions
        when the intention is to act as very thin wrapper over the shell. 
        There is just one MAJOR problem with it: 
        If the first character in the command is a quote (to escape a long path
        to the binary you are executing), then the limited (undesirable) parsing 
        built into the function can all fall apart.  So, this scrub function
        solves that...  
        """    
        if not cmd.startswith( __DBL_QUOTE ): return cmd
        cmdParts    = cmd[1:].split( __DBL_QUOTE )
        safeBinPath = _escapePath( cmdParts[0] )
        args        = __DBL_QUOTE.join( cmdParts[1:] ) # (the leading space will remain)
        return __SCRUB_CMD_TMPL.format( safeBinPath, args ) 
                     
    def _escapePath( path ):
        if not IS_WINDOWS: return path.replace(__SPACE, __ESC_SPACE)     
        return( path if __SPACE not in path else        
                __batchOneLinerOutput( __BATCH_ESCAPE_PATH_TMPLT.format(path) ) )    
    
    def __batchOneLinerOutput( batch ):
        cmd = __BATCH_ONE_LINER_TMPLT.format( batch )
        p = Popen( __BATCH_RUN_AND_RETURN_CMD, shell=False, 
                   startupinfo=__BATCH_ONE_LINER_STARTUPINFO,
                   stdin=PIPE, stdout=PIPE, stderr=PIPE )    
        # pipe cmd to stdin, return stderr, minus a trailing newline
        return p.communicate( cmd )[1].rstrip()  
    

    更新

    我最近想到了一个更好的 Windows 上下文技巧。您不需要任何转换为​​短文件名或空格的转义序列。您需要做的就是通过检查命令中的第一个字符是否是双引号来阻止 Python 源代码。好吧,在 Windows cmd / Batch 上,您可以在任何命令前加上 @ 以指示不要“回显”该行。所以,如果你只是在你的命令前面加上它,将不再有前导引号!您可能也不希望命令回显,因此无论如何它都是对自身的改进。

    基本上,将上面的__scrubSystemCmd 替换为以下内容。如果您愿意,您可以删除所有获得短文件名的代码(这就像我最初发布的代码的一半!)...

    if IS_WINDOWS: 
        ...
        __NO_ECHO_PREFIX = "@"
    
    def __scrubSystemCmd( cmd ):
        if IS_WINDOWS: 
            if not cmd.startswith( __NO_ECHO_PREFIX ): 
                return __NO_ECHO_PREFIX + cmd
        elif not cmd.startswith( __DBL_QUOTE ): return cmd
        cmdParts    = cmd[1:].split( __DBL_QUOTE )
        safeBinPath = _escapePath( cmdParts[0] )
        args        = __DBL_QUOTE.join( cmdParts[1:] ) # (the leading space will remain)
        return __SCRUB_CMD_TMPL.format( safeBinPath, args ) 
    

    【讨论】:

      【解决方案5】:

      您可以使用名称中包含空格的文件的short name。

      file = 'C:\\Exe\\FirstV~1\\filename.exe'
      os.system(file)
      

      【讨论】:

      • 你可以,但这可能不是一个好主意,特别是因为不同系统上的短名称可能不同。
      • 是的,这是一种方式,但是如果变量'file'是我刚刚写的,它也将作为arg传递给其他函数呢?
      猜你喜欢
      • 1970-01-01
      • 2015-06-03
      • 1970-01-01
      • 2017-04-12
      • 1970-01-01
      • 2021-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多