我正在 IPython 笔记本中交互式开发一个包装类。 该包装类调用用 java 编写的命令行程序,因此我需要访问用于编译该程序的同一版本的 java 运行时。
但是,我注意到在笔记本中使用方便的
!
运算符,会生成与在终端中使用 zsh
时不同的 shell 实例。 这已得到证实:
!which java # from within notebook cell
'/usr/bin/java'
$ which java # on my zsh terminal
/opt/homebrew/opt/openjdk@11/bin/java
运行
echo $PATH
也为每个人产生了不同的路径。
然后我遇到了 this StackOverflow 帖子,其中解释了
!
生成的子 shell 不会加载我的 .zshrc
文件(其中包含对在终端上使用 $PATH
时运行的 zsh
的修改) )。 但是,!
子shell会加载.zshenv
文件(如果存在),我已成功将其创建为符号链接:
ln -s ~/.zshrc ~/.zshenv
运行此命令后,
!
现在有效地使用与我在终端上使用的相同的zsh
,并且命令按预期工作,!which java
产生正确的结果:'/opt/homebrew/opt/openjdk@11/bin/java'
。
但是,我使用
!
作为测试和调试 shell 命令的便捷方法;但最终,我将这些 shell 命令调用移动到一个单独的 .py
文件中,我在其中使用 subprocess
模块。
当我使用
python3 script.py
从终端运行脚本时,一切都按预期运行;但是当将函数从 script.py
导入笔记本时,会产生与之前相同的问题。
作为一个最小的例子,来自笔记本:
!which java
/opt/homebrew/opt/openjdk@11/bin/java
这是正确的道路。 然而,
import subprocess
process = subprocess.run("which java", shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = process.stdout.decode("utf-8").strip()
output
给我:
'/usr/bin/java'
为什么
!
生成的 shell 能够访问我系统上与终端相同的 zsh
,但 subprocess
生成的 shell 却无法访问?
当您将
subprocess.run
(或相关函数)与 shell=True
一起使用时,它是显式使用 /bin/sh
。它不使用 your shell,也不会读取您的 zsh 初始化文件。你必须假设 /bin/sh
是一个通用的 Bourne 兼容 shell
像这样运行你的脚本是有效的...
python script.py
...因为脚本从您的 shell 继承了
$PATH
的值。您可以随时明确地致电zsh
;而不是:
process = subprocess.run("which java", shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
你可以运行:
process = subprocess.run(["zsh", "-c", "which java", check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
或者,如果您的脚本需要
$PATH
中的特定目录才能工作,请在调用 os.environ['PATH']
之前通过设置 subprocess.run
来显式设置。