2013-07-15 34 views
1

我正在製作一個python腳本,我需要使用一些設置在bash shell腳本中的環境變量。是否有任何模塊可以讀取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腳本並利用在內部發現的環境變量?目前我正在手工做這件事,這有點痛苦。

如果沒有這樣的模塊存在這樣做的正是我想要的東西,有沒有更Python(閱讀:容易/短)的手動分析一般文件的方式,現在我這樣做:

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 
+1

該腳本真的只是評論和'出口var =價值'線,或可能有其他東西在那裏? – abarnert

+0

特別是,在仔細閱讀這個之後......你需要'BUILDDIR = $ PWD'來評估shell的方式嗎?換句話說,BUILDDIR最終應該是'/ Users/Mike/src/testing'還是'$ PWD'? – abarnert

+0

@abarnert - 如你所述,還有其他的東西。我只是想更新我的'parse_bash_script()'方法,用於「BUILDDIR = $ PWD」等「特殊情況」...在我走下那條路線之前,我想看看我是否錯過了一些更好的選項 – Mike

回答

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 
+0

這不一定會正確處理引用,這是OP代碼明確做的(雖然只是部分成功),所以推測他需要它。例如,shell將在字符串中間處理加倍的引號。 – abarnert

+0

夠公平的。請參閱編輯 – inspectorG4dget

+0

首先,這仍然不能正確處理引用 - 儘管至少它與OP的原始代碼完全相同,而不是引入不同的錯誤。其次,你不應該在那裏有'strip'調用。 _Maybe_'var.lstrip()'和'val.rstrip()',但不是'strip'; 'foo = bar'不是sh中的任務。 – abarnert

3

有沒有模塊來做你想要的東西,但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 

這是一個有點hackier,但你也可以通過後期處理做到這一點少一點冗長。特別是,shlex會把export 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 

如果你想爲了更有趣,你可以構造一個對象,並且(a)直接從該文件驅動解析器,並且(b)以更細粒度的級別控制解析。但是,我認爲這不是必要的。


同時,如果你想處理環境替代(如BUILDDIR=$PWD暗示),這不會神奇地照顧這對你。您可以使configparser爲您的ExtendedInterpolation feature做到這一點,但是您需要欺騙configparser來處理shlex語法,此時......爲什麼要這麼做。

您當然可以通過編寫自己的插補器手動完成,但這很難做到正確。您需要知道殼牌關於爲什麼$PWD-foo${PWD}-foo相同的規則,但$PWD_foo${PWD_foo}等相同。

在這一點上的一個更好的解決方案 - 假設腳本實際上是安全運行的 - 將實際上使用shell來爲您執行。例如:

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 

當然這也將覆蓋之類的東西_=/usr/bin/env,但可能不是任何你所關心的。

+0

理想情況下,我想讓shell運行一些東西,但是當我執行'with ...'和'result = p.comm ...'時,我會回到''Traceback(最近的調用最後):文件'',第1行,在屬性錯誤:__exit __「'。在我看來,子進程不喜歡運行shell腳本 – Mike

+0

另外,是不是'subprocess.Popen(['sh'],'只是在我的Python會話內部打開一個新的shell,它本身在一個shell中?我不會'我認爲''sh'' shell中的變量會在我的python會話中「設置」 – Mike

+0

@Mike:這並不意味着子進程不喜歡運行shell腳本;這意味着Python不喜歡' Popen'作爲上下文管理器(又名「withy語句中的thingy」)。你使用的是什麼版本的Python?作爲[文檔](http://docs.python.org/3.3/library/subprocess.html#subprocess .Popen)表示,在Python 3.2中增加了上下文管理器支持,而在較早的版本中,你必須執行'p = subprocess.Popen(...)',然後確保正確關閉所有的東西,否則它是一樣的。 – abarnert

0

我有同樣的問題,並基於abarnert的建議,我決定實施解決方案作爲subprocess調用一個受限制的bash外殼,並結合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文檔:

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:

  • changing directories with cd
  • setting or unsetting the values of SHELL, PATH, ENV, or 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
  • importing function definitions from the shell environment at startup
  • parsing the value of SHELLOPTS from the shell environment at startup
  • redirecting output using the >, >|, <>, >&, &>, and >> redirection operators
  • using the exec builtin command to replace the shell with another command
  • adding or deleting builtin commands with the -f and -d options to the enable builtin command
  • Using the enable builtin command to enable disabled shell builtins
  • specifying the -p option to the command builtin command
  • turning off restricted mode with set +r or set +o restricted.
相關問題