首页 > 解决方案 > subprocess.Popen() 的每个实例都有自己的外壳吗?

问题描述

背景:我正在尝试制作一个通过 GUI (Tkinter)、Youtube_DL 和 FFmpeg 播放音乐的应用程序。实际应用程序完成后,它还需要 FFmpeg 作为环境变量才能工作。现在,我正在尝试确保创建“个人”FFmpeg 环境变量以使应用程序可移植(最后一步是使用 pyinstaller 制作 .exe 文件)。

问题:我正在尝试SET通过以下方式为 FFmpeg 创建环境变量subprocess.Popen

add_ffmpeg = subprocess.Popen(f"IF EXIST {path2set} SET PATH=%PATH%;{path2set}", shell=True)

当我尝试echo %PATH%(使用Popen)应该存在的 FFmpeg 变量时,不是。我只需要知道我是否在浪费时间,SET而是应该使用SETX或者其他一些解决方案,我愿意被告知我做错了。

相关代码:

# get any sub-directory with ffmpeg in it's name
ffmpeg = glob(f"./*ffmpeg*/")

# proceed if there is a directory
if len(ffmpeg) > 0:
    # double-check directory exists
    ffmpeg_exist = path.isdir(ffmpeg[0])

    if ffmpeg_exist:
        print("FFmpeg: Found -> Setting Up")
        
        # get the absolute path of the directories bin folder ".\ffmpeg-release-essentials.zip\bin"
        path2set = f"{path.abspath(ffmpeg[0])}\\bin\\"
        
        # add path of directory to environment variables
        add_ffmpeg = subprocess.Popen(f"IF EXIST {path2set} SET PATH=%PATH%;{path2set}", shell=True)
        add_ffmpeg.wait()
        
        # print all of the current environment variables
        list_vars = subprocess.Popen("echo %PATH%", shell=True)
        list_vars.wait()

else:
    print("FFmpeg: Missing -> Wait for Download...")
    
    # download the ffmpeg file via direct link
    wget.download("https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip")
    
    # unzip the file
    powershell = subprocess.Popen("powershell.exe Expand-Archive -Path './ffmpeg-release-essentials.zip' "
                                  "-DestinationPath './'")
    powershell.wait()

    # remove the file
    remove("./ffmpeg-release-essentials.zip")

    # get any sub-directory with ffmpeg in it's name
    ffmpeg = glob("./*ffmpeg*/")

    # double-check directory exists
    ffmpeg_exist = path.isdir(ffmpeg[0])
    
    # proceed with if it exists
    if ffmpeg_exist:
        print("FFmpeg: Found -> Setting Up")
        
        # get the absolute path of the directories bin folder ".\ffmpeg-release-essentials.zip\bin"
        path2set = f"{path.abspath(ffmpeg[0])}\\bin\\"
        
        # add path of directory to environment variables
        add_ffmpeg = subprocess.Popen(f"IF EXIST {path2set} SET PATH=%PATH%;{path2set}", shell=True)
        add_ffmpeg.wait()

        # print all of the current environment variables
        list_vars = subprocess.Popen("echo %PATH%", shell=True)
        list_vars.wait()
        
    else:
        print("Something unexplained has gone wrong.")
        exit(0)

标签: pythoncmdffmpegenvironment-variablespopen

解决方案


事实上,每个子进程都是一个单独的进程——这就是“子进程”的意思——这意味着如果你set在一次subprocess调用中进行了某些操作,它只会在该子进程中可见,而不是在它的父进程中,或者在你随后运行的其他子进程中。

一个常见的解决方案是将env关键字参数subprocess与您想要设置的任何变量一起传递给调用;

from pathlib import Path
from os import environ

env = environ.copy()

if Path(path2set).exists():
    env['PATH'] = '%s;%s' % (path2set, env['PATH'])

subprocess.Popen([...], env=env)

当然,在很多情况下,在父进程中进行这种修改就足够了,并且让任何子进程都继承了新的值。(直接修改os.environment,而不是复制,并env=env从最后一行删除)。

子进程是否是外壳是一个单独的主题,与您的问题无关。微不足道,有了shell=True它;但是就像在您的示例中一样,如果您的子进程是例如,powershell.exe那么它就是一个外壳,因为它就是这样(shell=True此外,它是相当多余的,并且可能是您希望尽可能避免的事情)。


推荐阅读