【问题标题】:Python Popen fails in compound command (PowerShell)Python Popen 在复合命令 (PowerShell) 中失败
【发布时间】:2017-12-18 19:48:08
【问题描述】:

我正在尝试使用 Python 的 Popen 更改我的工作目录并执行命令。

pg = subprocess.Popen("cd c:/mydirectory ; ./runExecutable.exe --help", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
buff,buffErr = pg.communicate()

但是,powershell 返回“系统找不到指定的路径”。路径确实存在。

如果我跑步

 pg = subprocess.Popen("cd c:/mydirectory ;", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)

它返回相同的东西。

但是,如果我运行这个:(不带分号)

pg = subprocess.Popen("cd c:/mydirectory",stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)

命令返回没有错误。这使我相信分号是问题。这种行为的原因是什么?我该如何解决?

我知道我只能执行 c:/mydirectory/runExecutable.exe --help,但我想知道为什么会这样。

更新:

我已经测试了将路径传递给 powershell 作为 Popen 的 executable 参数的参数。仅powershell.exe 可能还不够。要查找powershell 的真正绝对路径,请执行where.exe powershell。然后你可以将它传递给 Popen。请注意,shell 仍然正确。它将使用默认shell,但将命令传递给powershell.exe

powershell = C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
pg = subprocess.Popen("cd c:/mydirectory ; ./runExecutable.exe", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, executable=powershell)
buff,buffErr = pg.communicate()
//It works!

【问题讨论】:

    标签: python powershell subprocess popen


    【解决方案1】:

    在您的subprocess.Popen() 调用中,shell=True 表示应该使用平台的默认 shell。

    虽然 Windows 世界 - 值得称道的是 - 从 CMD (cmd.exe) 迁移到 PowerShell,但Python 根据 COMSPEC 环境变量确定要调用的 shell仍然指出cmd.exe,即使在最新的 W10 更新中,就 GUI 作为默认 shell 提供的功能而言,该更新已转向 PowerShell。

    为了向后兼容,这不会很快改变,也可能永远不会改变。

    因此,您的选择是:

    • 按照Maurice Meyer's 答案中的建议使用cmd 语法。

    • 不要不要使用shell = True并调用powershell.exe显式 - 见下文。

    • 仅限 Windows:在使用 shell = True 之前重新定义环境变量 COMSPEC - 见下文。


    一个简单的 Python 示例,说明如何直接调用 powershell 二进制文件,命令行开关后跟一个包含要执行的 PowerShell 源代码的字符串:

    import subprocess
    
    args = 'powershell', '-noprofile', '-command', 'set-location /; $pwd'
    subprocess.Popen(args)
    

    请注意,我特意使用powershell 而不是powershell.exe,因为一旦PowerShell Core 发布,这开启了该命令在Unix 平台上工作的可能性。 p>


    仅限 Windows:使用 shell = True 的示例,在重新定义环境变量 COMSPEC 以首先指向 PowerShell 之后:

    import os, subprocess    
    
    os.environ["COMSPEC"] = 'powershell'
    
    subprocess.Popen('Set-Location /; $pwd', shell=True)
    

    注意:

    • COMSPEC 仅在 Windows 上使用;在 Unix 平台上,shell 可执行文件总是 /bin/sh

    • 从 Windows PowerShell v5.1 / PowerShell Core v6-beta.3 开始,仅使用 -c(解释为 -Command)调用 powershell 仍会加载配置文件默认情况下,这可能会产生意想不到的副作用(上面使用了powershell 的显式调用,-noprofile 会抑制它)。

      • 将默认行为更改为加载配置文件是此GitHub issue 的主题,旨在使 PowerShell 的 CLI 与类似 POSIX 的 shell 的 CLI 保持一致。

    【讨论】:

    • 我正在尝试使用 powershell 作为默认的 python OS shell,但它总是加载配置文件。是否有解决方法?使用 python 的 executable=<path-to-powershell-exe> 使用 -NoProfile 参数?作为一种选择,也许可以操纵环境变量ComSpec?但是 IDK 是否有帮助,因为可能整个环境都丢失了?
    • @not2qubit ComSpec 应仅包含可执行文件路径,因此您将无法指定 -NoProfile。不确定您所说的环境丢失是什么意思。
    • 啊哈,好的。我的意思是修改 ComSpec 可能会影响后续调用。 IE。如果我在 Py 脚本中的某处设置 ComSpec,那么任何后续的操作系统调用仍然会有它,或者当指定不同的 executable 时,它是否会在新的 subprocess.Popen() 调用时重置? (我想它没有。)顺便说一句。我问这一切,因为this
    • @not2qubit 是的,任何后续使用shell = truesubprocess.Popen() 调用都会受到影响,更一般地说,任何子进程都会看到修改后的ComSpec 值。您可以本地化修改(在调用后恢复原始值),但这似乎不值得(并且它假设在修改后的值生效时没有并行运行创建子进程的代码)。
    【解决方案2】:

    您可以使用“&”字符而不是分号来连接多个命令。试试这个:

    pg = subprocess.Popen("cd c:/mydirectory & ./runExecutable.exe --help", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
    buff,buffErr = pg.communicate()
    

    【讨论】:

      猜你喜欢
      • 2012-01-07
      • 1970-01-01
      • 2020-12-31
      • 1970-01-01
      • 2013-08-06
      • 1970-01-01
      • 2019-07-01
      • 2015-10-23
      • 1970-01-01
      相关资源
      最近更新 更多