Python Popen 在复合命令 (PowerShell) 中失败

Jay*_* S. 4 python powershell subprocess popen

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

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

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

如果我跑

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

它返回同样的东西。

但是,如果我运行这个:(没有分号)

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

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

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

更新 :

我已经测试过将路径传递给 powershell 作为 Popenexecutable参数的参数。只是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!
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 5

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

值得称道的是,Windows 世界正在从 CMD ( cmd.exe) 转移到 PowerShell,而Python 根据COMSPEC环境变量确定要调用的 shell,即使在最新的 W10 更新中,它仍然指向cmd.exePowerShell 的GUI 内容。提供作为默认外壳。

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

因此,您的选择是:

  • 使用cmd语法,如Maurice Meyer 的回答中所建议的那样。

  • 千万不能使用shell = True和调用powershell.exe 明确的-见下文。

  • 仅限 WindowsCOMSPEC使用前重新定义环境变量shell = True- 见下文。


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

import subprocess

args = 'powershell', '-noprofile', '-command', 'set-location /; $pwd'
subprocess.Popen(args)
Run Code Online (Sandbox Code Playgroud)

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


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

import os, subprocess    

os.environ["COMSPEC"] = 'powershell'

subprocess.Popen('Set-Location /; $pwd', shell=True)
Run Code Online (Sandbox Code Playgroud)

笔记:

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

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

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