【问题标题】:How to use subprocess when multiple arguments contain spaces?当多个参数包含空格时如何使用子进程?
【发布时间】:2010-10-22 17:51:22
【问题描述】:

我正在开发一个包装脚本,该脚本将运行 vmware 可执行文件,允许虚拟机启动/关闭/注册/注销操作的自动化。我正在尝试使用子进程来处理调用可执行文件,但是子进程未正确处理可执行文件路径和可执行文件参数中的空格。下面是一段代码:

vmrun_cmd = r"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"
def vm_start(target_vm):
    list_arg = "start"
    list_arg2 = "hard"
    if vm_list(target_vm):
            p = Popen([vmrun_cmd, target_vm, list_arg, list_arg2],   stdout=PIPE).communicate()[0]
            print p
    else:
            vm_register(target_vm)
            vm_start(target_vm)
def vm_list2(target_vm):
    list_arg = "-l"
    p = Popen([vmrun_cmd, list_arg], stdout=PIPE).communicate()[0]
    for line in p.split('\n'):
            print line

如果我调用 vm_list2 函数,我会得到以下输出:

$ ./vmware_control.py --list                                                
C:\Virtual Machines\QAW2K3Server\Windows Server 2003 Standard Edition.vmx
C:\Virtual Machines\ubunturouter\Ubuntu.vmx
C:\Virtual Machines\vacc\vacc.vmx
C:\Virtual Machines\EdgeAS-4.4.x\Other Linux 2.4.x kernel.vmx
C:\Virtual Machines\UbuntuServer1\Ubuntu.vmx
C:\Virtual Machines\Other Linux 2.4.x kernel\Other Linux 2.4.x kernel.vmx
C:\Virtual Machines\QAClient\Windows XP Professional.vmx

如果我调用需要路径到 vm 参数的 vm_start 函数,我会得到以下输出:

$ ./vmware_control.py --start "C:\Virtual Machines\ubunturouter\Ubuntu.vmx"
'c:\Program' is not recognized as an internal or external command,
operable program or batch file.

显然,带有嵌入空格的第二个参数的存在正在改变子进程解释第一个参数的方式。有关如何解决此问题的任何建议?

python2.5.2/cygwin/winxp

【问题讨论】:

  • 为什么你在 c:/Program Files/VMware/VMware Server/vmware-cmd.bat 中的斜线走错了?不是 c:\Program Files\... 吗?
  • 嗯,cygwin 是 *nix 端口,所以它似乎喜欢标准(或我理解的标准)*nix 斜线符号。我的理解是子进程应该将分隔符转换为底层系统需要的任何内容。
  • 现在解决了吗?

标签: python subprocess


【解决方案1】:

对于最后三个我们的问题来说,这是一个相当困难的问题....到目前为止,没有任何说明有效,既没有使用 r"" 也没有使用带有列表的 Popen 等等。最终起作用的是格式字符串和 r"" 的组合。所以我的解决方案是这样的:

subprocess.Popen("{0} -f {1}".format(pathToExe, r'"%s"' % pathToVideoFileOrDir))

其中两个变量 pathToExe 和 pathToVideoFileOrDir 在其路径中都有空格。在格式化字符串中使用 \" 不起作用,并导致不再正确检测到第一个路径的相同错误。

【讨论】:

    【解决方案2】:

    一个问题是,如果命令用引号括起来并且没有空格,这也会混淆 shell。

    所以我这样做:

    if ' ' in raw_cmd:
        fmt = '"%s"'
    else:
        fmt = '%s'
    
    cmd = fmt % raw_cmd
    

    【讨论】:

      【解决方案3】:

      如果路径中有空格,我发现正确解释它们的最简单方法是这样。

      subprocess.call('""' + path + '""')
      

      我不知道为什么它需要双双引号,但这确实有效。

      【讨论】:

        【解决方案4】:

        这就是我不喜欢的地方

        vmrun_cmd = r"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"
        

        命令本身的名称中有空格——这让你的 shell 感到困惑。因此,“'c:\Program' 不被识别为内部或外部命令, 可运行的程序或批处理文件。”

        选项 1 - 将您的 .BAT 文件放在其他地方。事实上,将所有 VMWare 放在其他地方。规则如下:不要将“程序文件”目录用于任何内容。这是错误的。

        选项 2 -- 引用 vmrun_cmd

        vmrun_cmd = r'"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"'
        

        【讨论】:

          【解决方案5】:
          'c:\Program' is not recognized as an internal or external command,
          operable program or batch file.
          

          要收到此消息,您可以:

          1. 使用shell=True

            vmrun_cmd = r"c:\Program Files\VMware\VMware Server\vmware-cmd.bat"
            subprocess.Popen(vmrun_cmd, shell=True)
            
          2. 在代码的其他部分更改 vmrun_cmd

          3. 从 vmware-cmd.bat 中的某些内容中获取此错误

          要尝试的事情:

          • 打开python提示符,运行以下命令:

            subprocess.Popen([r"c:\Program Files\VMware\VMware Server\vmware-cmd.bat"])
            

          如果可行,那么引用问题是不可能的。如果没有,您已经隔离了问题。

          【讨论】:

          • 按顺序:1:我明确地远离设置 shell=True,所以不是这样。 2:vmrun_cmd 是一个全局常量,每次使用的方式完全相同。 3:没有。发生错误时尚未调用可执行文件 - 这是指定其路径的字符串的开头。
          【解决方案6】:

          我相信 list2cmdline(),它正在处理您的列表 args,除非 string 包含双引号,否则会在空格上拆分任何字符串 arg。所以我会期待

          vmrun_cmd = r'"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"'
          

          成为你想要的。

          您还可能希望将其他参数(如target_vm)用双引号括起来,假设它们也代表一个不同的参数以呈现给命令行。类似的东西

          r'"%s"' % target_vm
          

          (例如)应该适合。

          the list2cmdline documentation

          D'A

          【讨论】:

          • 好吧,target_vm 是一个争论,而不是一个常数,那么在这种情况下双引号的最佳方法是什么?
          • 我可能会将其表述为 r'"%s"' % target_vm
          • 伙计,这在评论中很难读懂。将其移至答案。
          • list2cmdline 文档链接已损坏,docs.python.org/2.6/library/subprocess.html 上几乎没有记录该内容——请注意,这是 2.6 版的文档,此后已从 docs.python.org/2.7/library/subprocess.html 中删除
          【解决方案7】:

          在 MS Windows 上的 Python 中,subprocess.Popen 类使用 CreateProcess API 来启动进程。 CreateProcess 采用字符串而不是参数数组之类的东西。 Python 使用 subprocess.list2cmdline 将 args 列表转换为 CreateProcess 的字符串。

          如果我是你,我会看看 subprocess.list2cmdline(args) 返回什么(其中 args 是 Popen 的第一个参数)。看看它是否在第一个参数周围加上引号会很有趣。

          当然,这种解释可能不适用于 Cygwin 环境。

          说了这么多,我没有MS Windows。

          【讨论】:

            【解决方案8】:

            可能是愚蠢的建议,但也许可以尝试以下方法,从等式中删除 subprocess + 空格:

            import os
            from subprocess Popen, PIPE
            
            os.chdir(
                os.path.join("C:", "Program Files", "VMware", "VMware Server")
            )
            
            p = Popen(
                ["vmware-cmd.bat", target_vm, list_arg, list_arg2],
                stdout=PIPE
            ).communicate()[0]
            

            也许也值得一试..

            p = Popen(
                [os.path.join("C:", "Program Files", "VMware", "VMware Server", "vmware-cmd.bat"), ...
            

            【讨论】:

              【解决方案9】:

              两件事

              1)

              您可能不想使用 Pipe 如果子程序的输出大于 64KB,那么您的进程很可能会崩溃。 http://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/

              2) Subprocess.Popen 有一个关键字参数 shell,使它好像 shell 一直在解析你的参数,设置 shell=True 应该做你想要的。

              【讨论】:

                【解决方案10】:

                你为什么使用 r""?我相信如果您从开头删除“r”,它将被视为可能包含空格的标准字符串。然后,Python 应该在将字符串发送到 shell 时正确地引用它。

                【讨论】:

                • 我已经检查过了,原始字符串状态不会改变行为。
                • 对 Windows 文件名使用 r"...\..." 是有意义的。
                猜你喜欢
                • 2018-07-08
                • 2011-10-04
                • 1970-01-01
                • 2016-03-01
                • 2011-08-26
                • 1970-01-01
                • 2018-05-21
                • 2018-05-30
                • 1970-01-01
                相关资源
                最近更新 更多