为什么 PATH 变量不影响 Windows 中的 subprocess.Popen

Why are PATH variables not effecting subprocess.Popen in Windows

我正在尝试使用 subprocess.Popen 从 Python 程序执行 .jar 文件并注意到我无法 link 到 [=56 的所需版本=] 而不指定完整路径。对于这个特定的应用程序,我觉得 Popen 是正确的选择,因为我想跟踪这个过程并确保它在我完成后消失,但我对其他选择持开放态度。

在下面的代码 JAVA_HOME 中,sys.pathos.environ['PATH'] 都已设置,以确保找到正确版本的 Java 可执行文件。如果未指定完整路径,Java 会报错“错误:找不到 java.dll”以及下面示例输出中显示的其他消息。此外,java.dll 在 C:\Program Files\Java\jdk1.8.0_221\bin 文件夹中。显然,当指定完整路径时,它会找到这些变量(尽管消息转到 STDERR?)。

我的问题是:为什么我的 PATH 变量不影响 Popen?

import subprocess
import sys
import os

# check for any references of java in os.environ
found = False
for k,v in os.environ.items():
    if 'java' in v.lower():
        print(f'java reference found in: {k}={v}')
        found = True
      
if not found:
    print('no java referenes found in os.environ')
print()


# Correct java version inserted into system path as first in the path
sys.path.insert(0, 'C:\Program Files\Java\jdk1.8.0_221\bin')
my_env = os.environ.copy()
# environ PATH set to find JDK 1.8 first
my_env["PATH"] = 'C:\Program Files\Java\jdk1.8.0_221\bin;' + my_env["PATH"]
# JAVA_HOME set and bin folder here has java.dll
my_env["JAVA_HOME"] = '"C:\Program Files\Java\jdk1.8.0_221\bin"'

# looking at where java.exe is found from Popen on Windows
p = subprocess.Popen(['where', 'java.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=my_env)
stdout,stderr = p.communicate()
print('where java.exe')
print('STDOUT: ' + stdout.decode('utf-8'), end='')
print('STDERR: ' + stderr.decode('utf-8'))
print()

# java.exe -version command does not work
p = subprocess.Popen(['java.exe', '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=my_env)
stdout,stderr = p.communicate()
print('java.exe -version')
print('STDOUT: ' + stdout.decode('utf-8'))
print('STDERR: ' + stderr.decode('utf-8'))

# however, when full path is specified it works
p = subprocess.Popen(['C:\Program Files\Java\jdk1.8.0_221\bin\java.exe', '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=my_env)
stdout,stderr = p.communicate()
print('C:\Program Files\Java\jdk1.8.0_221\bin\java.exe -version')
print('STDOUT: ' + stdout.decode('utf-8'))
print('STDERR: ' + stderr.decode('utf-8'))

# I can see the output here is the correct version
os.system('java.exe -version')

产生输出:

no java referenes found in os.environ

where java.exe
STDOUT: C:\Program Files\Java\jdk1.8.0_221\bin\java.exe
C:\Windows\System32\java.exe
STDERR: 

java.exe -version
STDOUT: 
STDERR: Error: Registry key 'Software\JavaSoft\Java Runtime Environment'\CurrentVersion'
has value '1.8', but '1.7' is required.
Error: could not find java.dll
Error: Could not find Java SE Runtime Environment.

C:\Program Files\Java\jdk1.8.0_221\bin\java.exe -version
STDOUT: 
STDERR: java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)

我在 Windows 10 上使用 Python 3.8.8。正在尝试 link 到 Java 1.8.0_221。我确定我在这里做错了什么,但不确定是什么。

更新:自发布以来,我在评论部分尝试了以下其他解决方案。

  1. 使用 Popen env 参数和 os.environ

    的副本
  2. 在 java 路径 ex:

    周围添加了引号

    my_env["PATH"] = '"C:\Program Files\Java\jdk1.8.0_221\bin";' + my_env["PATH"] - 根据一些测试,我认为至少 os.envirion['PATH'].

    不需要这样做
  3. 从 Windows GUI 的 Windows 系统 PATH 变量中删除了对 Java 的所有引用。甚至在代码开头进行检查以验证 Java 引用不在 os.environ.

仍然没有运气。

感谢 VGR 的评论,我对子流程文档 here 中的大红色警告框采取了更深入的研究方法,其中指出。

For Windows, see the documentation of the lpApplicationName and lpCommandLine parameters of WinAPI CreateProcess, and note that when resolving or searching for the executable path with shell=False, cwd does not override the current working directory and env cannot override the PATH environment variable. Using a full path avoids all of these variations.

我想我没有完全理解最后一句话,它在 Windows 中说 PATH 不会被覆盖,应该使用可执行文件的完整路径以避免可执行文件名称之间的混淆。这种行为解释了我所看到的。