我现在正在制作一个python脚本,我需要使用一些在bash shell脚本中设置的环境变量。
bash脚本类似于:
#! /bin/sh
#sets some names:
export DISTRO="unified"
#export DISTRO="other"
#number of parallel builds
export BB_NUM_THREADS=2
#set build dir
export BUILDDIR=$PWD
通常情况下,我只是用bash来源这个脚本,然后去做我的构建。我试图在整个过程中包装python来对输出进行一些管理,所以我想删除手册source ./this_script.sh
步骤。
我想要做的是从python中读取此脚本,然后使用os.environ
在其中设置变量。 (我知道这不会影响父级,但只影响当前运行的Python实例,这很好)
所以为了让我的工作更轻松,我试图找出是否有任何模块可以“解析”bash脚本并利用其中的环境变量?目前我手动这样做,这有点痛苦。
如果没有这样的模块可以完全我想要的东西,是否有一个更加pythonic(读取:更简单/更短)的方式手动解析文件,现在我正在这样做:
def parse_bash_script(fn):
with open(fn) as f:
for line in f:
if not line[:1] == '#': #ignore comments
if "export" in line:
line = line.replace(" ","").strip()
var = line[6:line.find("=")]
val = line[line.find("=")+1:len(line)]
if "\"" in val:
val = val[1:-1]
os.environ[var]=val
答案 0 :(得分:4)
没有任何模块可以完全按照您的意愿行事,但shlex
会做很多你想要的事情。特别是,它将获得quoting等权利,而不必担心它(这是最难的部分),以及跳过评论等。它唯一不会做的是处理export
个关键字。
简单的方法就是预处理:
with open(fn) as f:
processed = f.read().replace('export ', '')
for line in shlex.split(processed):
var, _, value = line.partition('=')
os.environ[var] = val
这有点令人讨厌,但你也可以通过后期处理来减少冗长。特别是,shlex
会将export foo="bar spam eggs"
视为两个值:export
和foo="bar spam eggs"
,您可以跳过== 'export'
或分区找到的值没什么,或者......例如:
with open(fn) as f:
for line in shlex.split(f.read()):
var, eq, value = line.partition('=')
if eq:
os.environ[var] = val
如果你想变得更加漂亮,你可以构造一个shlex
对象和(a)直接从文件驱动解析器,以及(b)控制更细粒度级别的解析。但是,我认为这不是必要的。
同时,如果你想要处理环境替换(如BUILDDIR=$PWD
所暗示的那样),那么这对你来说不会神奇地处理。您可以configparser
使用ExtendedInterpolation
feature为configparser
执行此操作,但是您需要欺骗shlex
处理$PWD-foo
语法,此时......为什么要这么麻烦。< / p>
您当然可以通过编写自己的插补器手动完成,但这很难做到。您需要了解shell的规则,为什么${PWD}-foo
与$PWD_foo
相同,但${PWD_foo}
与with open('script.sh') as f:
script = f.read()
script += b'\nenv'
with subprocess.Popen(['sh'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) as p:
result = p.communicate(script)
for line in result.splitlines():
var, _, value = line.partition('=')
os.environ[var] = value
相同,等等。
此时更好的解决方案 - 假设脚本实际上可以安全运行 - 将实际使用shell为您执行此操作。例如:
_=/usr/bin/env
当然,这也会覆盖{{1}}之类的内容,但可能不是你关心的任何内容。
答案 1 :(得分:1)
def parse_bash_script(fn):
with open(fn) as f:
for line in f:
if not line.startswith('#'): #ignore comments
if "export" in line:
var, _, val = line.partition('=')
var = var.lstrip()
val = val.rstrip()
if val.startswith('"'):
vals = val.rpartition('"')
val = vals[0][1]+vals[2]
os.environ[var]=val
答案 2 :(得分:0)
我遇到了同样的问题,根据abarnert的建议,我决定将解决方案实现为对受限制的bash shell的子进程调用,并结合shlex。
import shlex
import subprocess
filename = '/path/to/file.conf'
o, e = subprocess.Popen(
['/bin/bash', '--restricted', '--noprofile', '--init-file',
filename, '-i', '-c', 'declare'],
env={'PATH': ''},
stdout=subprocess.PIPE,
stderr=subprocess.PIPE).communicate()
if e:
raise StandardError('conf error in {}: {}'.format(filename, e))
for token in shlex.split(o):
parts = token.split('=', 1)
if len(parts) == 2:
os.environ[parts[0]] = parts[1]
受限shell的优点是它可以阻止执行shell脚本时可能发生的许多不良或恶意副作用。来自bash文档:
受限制的shell用于设置比标准shell更受控制的环境。它的行为与bash相同,但不允许或不执行以下操作:
- 用cd
更改目录- 设置或取消设置SHELL,PATH,ENV或BASH_ENV
的值- 指定包含/
的命令名称- 指定包含/作为参数的文件名。内置命令
- 指定包含斜杠的文件名作为hash builtin命令的-p选项的参数
- 在启动时从shell环境导入函数定义
- 在启动时从shell环境解析SHELLOPTS的值
- 使用&gt;,&gt; |,&lt;&gt;,&gt;&amp;,&amp;&gt;和&gt;&gt;重定向输出重定向运算符
- 使用exec builtin命令将shell替换为另一个命令
- 使用-f和-d选项向enable builtin命令添加或删除内置命令
- 使用enable builtin命令启用禁用的shell内置
- 为命令内置命令
指定-p选项- 使用set + r或set + o restricted关闭限制模式。