3

为什么我的 python 解释器中的 os.system('command') 的输出与终端中的命令不同?

问题解释得很快:

我有

echo $CONFPATH
/home/claramart/Datamart/Parameter

os.system('echo $CONFPATH')

0

这是为什么?

详细信息: 我想获得我的环境 $CONFPATH。我正在使用 python3.5 和 ubuntu16.04.2。

我可以从命令行执行此操作:

echo $CONFPATH
/home/claramart/Datamart/Parameter

这就是我想要的答案。

从命令行将其作为 python 命令执行也可以:

python3 -c 'import os; print(os.environ["CONFPATH"])'
/home/claramart/Datamart/Parameter

问题是,我想从我的 python 解释器而不是从命令行执行它。从我的 python 解释器执行它不起作用(我使用的是 Pyzo4.4.1):

print(os.environ["CONFPATH"])
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/lib/python3.5/os.py", line 725, in __getitem__
    raise KeyError(key) from None
KeyError: 'CONFPATH'

我想这完全来自我的解释器,而不是 python 本身,因为从命令行执行 python 是有效的。此外,我可以从我的 python 解释器中获取 $PYTHONPATH ,所以我猜它根本没有检测到所有环境变量。

为了避免这种情况并从命令行执行它,我想从我的 python 解释器执行命令行,但是我的 2 个命令行执行都没有按照我想要的方式工作:

os.system('echo $CONFPATH')

0

和 :

os.system("""python3 -c 'import os; print(os.environ["CONFPATH"]'""")
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3.5/os.py", line 725, in __getitem__
    raise KeyError(key) from None
KeyError: 'CONFPATH'
256

再一次,在这两种情况下,它都适用于 $PYTHONPATH,所以我想它必须在某个时候通过我的解释器,因为我的问题是特定于该变量 $CONFPATH。

为什么我的 python 解释器中的 os.system('command') 的输出与终端中的命令不同?

4

2 回答 2

4

我认为你期望有一个环境。事实是,每个进程都有自己的环境,通常继承自其父进程。不幸的是,我在你的片段中没有看到足够的信息来告诉你如何传递这个特定的值,但我们可以通过它们来看看它们实际上在说什么。

echo $CONFPATH
/home/claramart/Datamart/Parameter

这显示了一个 shell 命令echo,表明 shell 可以扩展参数$CONFPATH。但是,它没有显示这是来自 shell 还是环境变量。不过,后面的片段确实表明您确实有一个设置它的环境。

os.system('echo $CONFPATH')

0

这是一个 Python 函数调用,反过来又调用了一个 C 库函数,这会导致生成一个新的 shell 并解释给定的命令。值得注意的是,这与您运行的任何 shell 都不相同。它是一个新的 /bin/sh 进程,它从进行调用的 Python 解释器继承环境。我们看到这个 shell 命令成功(退出值 0)并将 CONFPATH 扩展为空,表明它是空的或未设置Python解释器的环境。

python3 -c 'import os; print(os.environ["CONFPATH"])'
/home/claramart/Datamart/Parameter

下面是一个启动 Python 解释器的 shell 命令示例,它使用命令行来打印环境变量。它成功了,因为变量是从您运行命令的 shell 继承的。

os.system("""python3 -c 'import os; print(os.environ["CONFPATH"]'""")
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/lib/python3.5/os.py", line 725, in __getitem__
    raise KeyError(key) from None
KeyError: 'CONFPATH'
256

这里一个被另一个包裹着;从 Python 解释器开始,shell 开始运行一个命令,该命令启动另一个 Python 解释器,该解释器应该打印 CONFPATH 环境变量。但是,此内部 Python 代码失败,在其环境中找不到 CONFPATH 时引发 KeyError 异常。这与 shell 的行为相反,后者只是显示一个空值。由于没有捕获到异常,因此打印了一个回溯,Python shell 返回了一个错误代码,然后由 subshel​​l 返回,最后由我们的外部 Python 解释器打印为 256。

您已经展示了从两个不同的环境中运行的命令:一个设置了 CONFPATH 的 shell,一个没有设置 CONFPATH 的 Python 解释器。pstreeps f或者ps -H可以帮助您可视化进程树,从而可视化继承环境的位置。注意环境是从父进程拷贝过来的;在父母中改变它只会影响新的孩子,而不是现有的孩子。

在 Linux 中,也可以在 /proc 文件系统中找到环境。例如,tr \\0 \\n < /proc/$$/environ打印运行它的 shell 的环境(shell 将 $$ 扩展为它自己的进程 ID)。

当您从不同的环境运行事物时,这种区别变得更加重要。例如,通过您的.profile.bashrc文件设置的任何内容都不会影响从 运行的命令cron,与系统启动脚本类似。大多数程序保持环境不变,但有些程序会出现特定的例外情况,例如 setuid 程序忽略 LD_LIBRARY_PATH 或su重写env环境。

于 2017-07-27T10:31:11.400 回答
1

尝试从父 shell导出shell 变量,即启动 Python 的 shell:

$ CONFPATH=/home/claramart/Datamart/参数
$ 回声 $CONFPATH
/home/claramart/Datamart/参数
$ 环境 | grep 配置路径
$ python3 -c '导入操作系统;print(os.environ["CONFPATH"])'
回溯(最近一次通话最后):
  文件“”,第 1 行,在
  文件“/usr/lib64/python3.5/os.py”,第 725 行,在 __getitem__
    从无提高 KeyError(key)
键错误:'CONFPATH'
$ python3 -c '导入操作系统;print(os.system("echo $CONFPATH"))'

0

# 导出变量后尝试相同的操作
$导出CONFPATH
$ 环境 | grep 配置路径
CONFPATH=/home/claramart/Datamart/参数
$ python3 -c '导入操作系统;print(os.environ["CONFPATH"])'
/home/claramart/Datamart/参数
$ python3 -c '导入操作系统;print(os.system("echo $CONFPATH"))'
/home/claramart/Datamart/参数
0

Shell 变量默认情况下不会导出,因此在上述export命令之前,CONFPATH父进程(您的终端的 shell)实际上并未导出。如上所示,您的 Python 进程根本不应该CONFPATH在其环境中定义。

话虽如此,我很惊讶地发现在其中查找环境变量os.environ显然对您有效,而对您os.system()无效。两者都应该工作,或者都不应该工作,这取决于环境变量的可用性。也许这是使用 Pyzo 或您(或 Pyzo)调用解释器的方式的一个怪癖。

于 2017-07-27T12:04:00.743 回答