从Python程序调用脚本切换pyenv虚拟环境时遇激活失败问题
我有一个名为foo的bash脚本,内容如下:
#!/bin/bash pyenv activate my_env python main.py
通过. foo或source foo调用该脚本时,能正常切换pyenv虚拟环境并运行main.py。
现在我想从Python程序中调用这个脚本,编写了如下代码:
import os import subprocess def main() -> None: script_path = 'scripts/foo' shell_source(script_path) def shell_source(script): pipe = subprocess.Popen( ". %s && env -0" % script, stdout=subprocess.PIPE, shell=True ) output = pipe.communicate()[0].decode('utf-8') output = output[:-1] env = {} for line in output.split('\x00'): line = line.split('=', 1) env[line[0]] = line[1] os.environ.update(env) if __name__ == '__main__': main()
但运行该Python程序时出现错误:
Failed to activate virtualenv.
Perhaps pyenv-virtualenv has not been loaded into your shell properly.
Please restart current shell and try again.
请问该如何解决这个问题?
出现这个错误的核心原因是:subprocess.Popen默认启动的是非交互式shell,不会加载~/.bashrc或~/.profile这类配置文件,而pyenv通常是在这些文件里完成初始化的,导致shell找不到pyenv的命令和虚拟环境激活逻辑。
可以通过以下几种方式解决:
方法1:显式加载pyenv初始化脚本
修改subprocess.Popen的命令,先加载pyenv的配置文件,再执行目标脚本。比如pyenv初始化在~/.bashrc里的话,代码改成:
pipe = subprocess.Popen( "source ~/.bashrc && . %s && env -0" % script, stdout=subprocess.PIPE, shell=True, executable="/bin/bash" # 指定用bash shell,避免默认sh的兼容性问题 )
如果你的pyenv是在~/.profile或~/.bash_profile里配置的,替换成对应的文件路径即可。
方法2:直接调用虚拟环境的Python解释器
既然最终目的是运行main.py,可以跳过pyenv activate步骤,直接调用虚拟环境里的Python路径执行脚本。
先找到my_env虚拟环境的Python路径,通常是~/.pyenv/versions/my_env/bin/python,然后修改Python代码:
def main() -> None: python_path = os.path.expanduser("~/.pyenv/versions/my_env/bin/python") script_path = "main.py" # 填写main.py的正确路径 subprocess.run([python_path, script_path], check=True)
这种方式更直接,避免了shell环境变量传递的复杂问题。
方法3:启动交互式shell
给shell传递-i参数,强制启动交互式bash,它会自动加载~/.bashrc配置:
pipe = subprocess.Popen( ". %s && env -0" % script, stdout=subprocess.PIPE, shell=True, executable="/bin/bash -i" )
不过这种方式可能会引入一些不必要的环境变量,不如前两种方法干净。
内容的提问来源于stack exchange,提问作者Psionman

