【问题标题】:Piping variables from shell script to Python [duplicate]从shell脚本到Python的管道变量[重复]
【发布时间】:2017-08-11 19:15:07
【问题描述】:

我有一个名为“script.sh”的脚本,其内容是:

#!/bin/sh
export A=5

我想从 within python(实际上是 iPython)执行这个脚本并读取变量 'A'。

import os
import subprocess

subprocess.call('./script.sh')
A=os.environ['A']

不幸的是,这似乎不起作用,给我一个找不到 A 的错误。如果我理解正确的话,子进程实际上是在与 os.environ 查询的 shell 不同的 shell 中运行的。但是为什么我不能运行类似的东西:

subprocess.call('echo $A')

?

我应该改变什么来完成这项工作?一般来说,我只是想从脚本中获取“A”的值,最好是通过某种形式的shell执行脚本(实际脚本很长)。

要了解更多信息,该脚本将包含登录凭据,因此理想情况下,我想要一种安全、简约的方式来访问它们的值。

【问题讨论】:

  • 您无能为力。子外壳不能在其父外壳中设置环境变量。
  • @DanielRoseman:对,我刚刚添加了一个编辑。有没有办法从子进程外壳访问它?那是我没有得到的部分
  • 是的,您在subprocess.call 生成的shell 中成功设置了环境变量,然后在您的脚本运行后立即消失。
  • 子进程每次都使用不同的shell实例。
  • os.environ 在第一次导入 os 时被捕获。即使它不是子进程,它也行不通。 docs.python.org/3/library/os.html#os.environ

标签: python bash subprocess


【解决方案1】:

您需要在子shell 中获取脚本(因此它在同一个shell 进程中设置变量),然后在该子shell 中回显变量:

a = subprocess.check_output('source ./script.sh; echo "$A"', shell=True)

然后就可以从管道中读取得到变量的值了。

【讨论】:

  • 感谢您的回答。我不是这方面的专家,但据我了解,shell=true 和 stdout=PIPE 在我看到的答案中是不鼓励的。具体来说,如果我的脚本包含凭据,这仍然是一个好的解决方案吗?
  • 我将它从 subprocess.call 更改为 subprocess.check_output。那还是灰心丧气吗?如果是这样,您应该使用Popen()communicate
  • 我看不出脚本中的凭据与此有何关联。
  • 如果它包含凭据,则根本不应该exporting 他们,除非您确定您的操作系统保护环境变量不被从进程元数据中读取(现代系统会这样做,很多旧的没有)。
  • @CharlesDuffy 我以为他的意思是脚本中的其他地方有凭据,而不是 A 的值是凭据,但您可能是对的。
【解决方案2】:

诀窍是生成一个新的 shell 并告诉它解释您的脚本代码,并以 Python 可以读取的方式打印出它的环境。单线:

In [10]: subprocess.check_output(["bash", "-c", "source ./script.sh; env"])
Out[10]: '...\nA=5\n...'

发生了什么:一般情况下,环境变量是在程序开始时设置的,任何子进程都不能修改其父进程的环境;这是一种沙盒。但是source 是一个 bash 内置函数,其中 bash 说“不是将 script.sh 作为无法修改我的环境的新(子)子进程生成,而是以我自己的身份运行代码行(bash)并相应地修改我的环境未来的命令”。并且添加了env,以便 bash 打印由换行符分隔的环境。 check_output 只是抓取该输出并将其带回 Python。

(作为旁注,source 命令是您用来更新 shell 以使用某个 virtualenv:source my_project/bin/activate。然后您当前 shell 的 $PATH 和其他变量被更新以使用 virtualenv python和该会话其余部分的库。你不能只说my_project/bin/activate,因为它会将它们设置在一个子shell中,什么都不做:))

【讨论】:

  • 环境变量可以包含文字换行符。因此,这不是表示和解析它们的安全方式。
  • (考虑攻击者是否能够设置一个环境变量,比如通过 CGI 的自动 header->variable 机制,其值类似于 CGI_X_evil=$'hello\nLD_PRELOAD=/tmp/exploit.so' 创建的值——如果代码解析env 看到了LD_PRELOAD=/tmp/exploit.so,然后用它更新了你的 Python 程序的环境,你的 Python 工具调用的未来程序将运行任意代码。
  • @CharlesDuffy 一般来说,这是一个好点。 OP 只是试图更新他们的 Python 程序的环境作为达到目的的一种手段。正如他们解释的那样,他们要做的就是读取脚本创建的环境,并将其作为 Python 中的字符串获取。没有人谈论使用该输出作为任何未来程序或当前程序的环境。只是提取受信任的环境变量。