简体   繁体   English

是否有任何模块可以读取Shell脚本?

[英]Are there any modules to read shell scripts?

I'm making a python script right now, and I need to use some environment variables which are set in a bash shell script. 我现在正在制作python脚本,我需要使用在bash shell脚本中设置的一些环境变量。

The bash script is something like: 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

Normally, I would just source this script in bash, then go do my builds. 通常,我只是在bash中获取此脚本,然后进行构建。 I'm trying to wrap python around the whole process to do some management of the output so I want to remove the manual source ./this_script.sh step. 我正在尝试将python包装在整个过程中,以对输出进行一些管理,因此我想删除手动source ./this_script.sh步骤。

What I want to do is read this script from python and then use os.environ to set up the variables within it. 我想做的是从python读取此脚本,然后使用os.environ在其中设置变量。 (I know this will not affect the parent, but only the current running Python instance and that's fine) (我知道这不会影响父对象,只会影响当前正在运行的Python实例,这样就可以了)

So to make my work easier, I'm trying to find out are there any modules which can "parse" the bash script and make use of the environment variables found within? 为了使我的工作更轻松,我试图找出是否有任何模块可以“解析” bash脚本并利用其中的环境变量? Currently I'm doing this by hand and it's a bit of a pain. 目前,我正在手动执行此操作,这有点痛苦。

If no such module exists to do exactly what I want, is there a more pythonic (read: easier/shorter) way of manually parsing a file in general, right now I'm doing this: 如果没有这样的模块可以完全实现我想要的功能,那么一般情况下,是否存在更多的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

There is no module to do exactly what you want, but shlex will do a lot of what you want. 没有模块可以完全满足您的需求,但是shlex您的许多需求。 In particular, it will get the quoting , etc. right without you having to worry about it (which is the hardest part of this), as well as skipping comments, etc. The only thing it won't do is handle the export keywords. 特别是,它将得到正确的引号等,而您不必担心它(这是其中最难的部分)以及跳过注释等。它唯一不会做的就是处理export关键字。

The easy way around that is to preprocess: 解决这个问题的简单方法是进行预处理:

with open(fn) as f:
    processed = f.read().replace('export ', '')
for line in shlex.split(processed):
    var, _, value = line.partition('=')
    os.environ[var] = val

It's a bit hackier, but you can also do it a bit less verbosely by post-processing. 它有点hacker,但是您也可以通过后处理来减少冗长的操作。 In particular, shlex will treat export foo="bar spam eggs" as two values: export and foo="bar spam eggs" , and you can just skip the ones that == 'export' , or where the partition finds nothing, or… For example: 特别地, shlexexport foo="bar spam eggs"视为两个值: exportfoo="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

If you want to get fancier, you can construct a shlex object and (a) drive the parser directly from the file, and (b) control the parsing at a finer-grained level. 如果您想变得更高级,可以构造一个shlex对象,并(a)直接从文件驱动解析器,并且(b)在更细粒度的级别控制解析。 However, I don't think that's necessary here. 但是,我认为这不是必需的。


Meanwhile, if you want to handle environment substitution (as the BUILDDIR=$PWD implies), this won't magically take care of that for you. 同时,如果您要处理环境替换(如BUILDDIR=$PWD所暗示的),那么这将不会为您神奇地解决问题。 You can make configparser do that for you with its ExtendedInterpolation feature , but then you'll need to trick configparser into handling shlex syntax, at which point… why bother. 您可以使用它的ExtendedInterpolation功能使configparser做到这一点,但是随后您将需要诱使configparser处理shlex语法,这时……何必麻烦。

You can of course do it manually by writing your own interpolator, but that's hard to get right. 当然,您可以通过编写自己的插值器来手动完成此操作,但这很难做到。 You need to know the shell's rules for why $PWD-foo is the same as ${PWD}-foo , but $PWD_foo is the same as ${PWD_foo} , etc. 您需要了解为什么$PWD-foo${PWD}-foo ,但是$PWD_foo${PWD_foo}相同的shell规则。

A better solution at this point—assuming the script is actually safe to run—would be to actually use a shell to do it for you. 在这一点上,一个更好的解决方案(假设脚本实际上可以安全运行)将是实际上使用shell为您完成此操作。 For example: 例如:

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

Of course this will also override things like _=/usr/bin/env , but probably not anything you care about. 当然,这也会覆盖_=/usr/bin/env东西,但可能与您无关。

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

I had the same problem, and based on the advice from abarnert, I decided to implement the solution as a subprocess call to a restricted bash shell, combined with shlex. 我遇到了同样的问题,并且根据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]

The advantage to the restricted shell is that it blocks many of the undesirable or malicious side effects that may otherwise happen when executing a shell script. 受限Shell的优点在于,它可以阻止执行Shell脚本时可能发生的许多不良或恶意副作用。 From the bash documentation: 从bash文档中:

A restricted shell is used to set up an environment more controlled than the standard shell. 受限外壳用于设置比标准外壳更受控制的环境。 It behaves identically to bash with the exception that the following are disallowed or not performed: 它的行为与bash相同,不同之处在于不允许或不执行以下操作:

  • changing directories with cd 用cd更改目录
  • setting or unsetting the values of SHELL, PATH, ENV, or BASH_ENV 设置或取消设置SHELL,PATH,ENV或BASH_ENV的值
  • specifying command names containing / 指定包含/的命令名称
  • specifying a file name containing a / as an argument to the . 指定一个包含/的文件名作为。 builtin command 内置命令
  • Specifying a filename containing a slash as an argument to the -p option to the hash builtin command 将包含斜杠的文件名指定为hash内置命令的-p选项的参数
  • importing function definitions from the shell environment at startup 启动时从外壳环境导入函数定义
  • parsing the value of SHELLOPTS from the shell environment at startup 启动时从shell环境中解析SHELLOPTS的值
  • redirecting output using the >, >|, <>, >&, &>, and >> redirection operators 使用>,> |,<>,>&,&>和>>重定向运算符重定向输出
  • using the exec builtin command to replace the shell with another command 使用exec内置命令将外壳替换为另一个命令
  • adding or deleting builtin commands with the -f and -d options to the enable builtin command 在启用内置命令中添加或删除带有-f和-d选项的内置命令
  • Using the enable builtin command to enable disabled shell builtins 使用enable buildin命令启用禁用的shell内置程序
  • specifying the -p option to the command builtin command 在命令内置命令中指定-p选项
  • turning off restricted mode with set +r or set +o restricted. 使用设置+ r或设置+ o限制关闭受限模式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM