2010-08-17 11 views
63

Ich habe ein Skript, das ungefähr so ​​aussieht:Emulating Bash ‚Quelle‘ in Python

export foo=/tmp/foo           
export bar=/tmp/bar 

Jedes Mal, wenn ich baue ich ‚source init_env‘ laufen (wo init_env das obige Skript ist) einige Variablen einzurichten .

Um das gleiche in Python zu bewerkstelligen ich diesen Code hatte ausgeführt,

reg = re.compile('export (?P<name>\w+)(\=(?P<value>.+))*') 
for line in open(file): 
    m = reg.match(line) 
    if m: 
     name = m.group('name') 
     value = '' 
     if m.group('value'): 
      value = m.group('value') 
     os.putenv(name, value) 

Aber dann jemand entschieden, wäre schön, eine Zeile wie die folgende in die init_env Datei hinzuzufügen:

export PATH="/foo/bar:/bar/foo:$PATH"  

Offensichtlich fiel mein Python-Skript auseinander. Ich könnte das Python-Skript ändern, um mit dieser Zeile umzugehen, aber dann bricht es später, wenn jemand mit einer neuen Funktion kommt, um in der init_env Datei zu verwenden.

Die Frage ist, ob es eine einfache Möglichkeit gibt, einen Bash-Befehl auszuführen und lassen Sie es meine os.environ ändern?

+0

verwandt: [Aufrufen des "source" -Befehls aus subprocess.Popen] (http://stackoverflow.com/q/7040592/4279) – jfs

Antwort

78

Das Problem mit Ihrem Ansatz ist, dass Sie versuchen, Bash-Skripte zu interpretieren. Zuerst versuchen Sie einfach, die Exportanweisung zu interpretieren. Dann bemerken Sie, dass Leute eine variable Erweiterung verwenden. Spätere Leute werden Conditionals in ihre Dateien einfügen oder sie ersetzen. Am Ende wirst du einen ausgewachsenen Bash-Skript-Interpreter mit einer ganzen Reihe von Bugs haben. Tu das nicht.

Lassen Sie Bash die Datei für Sie interpretieren und sammeln Sie dann die Ergebnisse.

Sie können es wie folgt tun:

#! /usr/bin/env python 

import os 
import pprint 
import subprocess 

command = ['bash', '-c', 'source init_env && env'] 

proc = subprocess.Popen(command, stdout = subprocess.PIPE) 

for line in proc.stdout: 
    (key, _, value) = line.partition("=") 
    os.environ[key] = value 

proc.communicate() 

pprint.pprint(dict(os.environ)) 

Vergewissern Sie sich, dass Sie Fehler bei bash Griff nicht zu source init_env oder bash selbst nicht ausführen, oder subprocess nicht bash oder andere Fehler auszuführen.

Lesen Sie die Dokumentation auf subprocess für weitere Details.

Hinweis: Dies erfasst nur Variablen, die mit der export-Anweisung festgelegt wurden, da env nur exportierte Variablen druckt.

Genießen.

Beachten Sie, dass die Python documentation besagt, dass wenn Sie die Umgebung manipulieren möchten, os.environ direkt anstelle von os.putenv() manipulieren sollten. Ich halte das für einen Fehler, aber ich schweife ab.

+0

Schön das war was ich gesucht habe. Danke – getekha

+10

Wenn Sie nicht exportierte Variablen interessieren und das Skript sich außerhalb Ihrer Kontrolle befindet, können Sie set -a verwenden, um alle Variablen als exportiert zu markieren. Ändern Sie den Befehl einfach in: ['bash', '-c', 'set -a && source init_env && env'] – ahal

+0

Beachten Sie, dass dies bei exportierten Funktionen fehlschlägt. Ich würde es lieben, wenn Sie Ihre Antwort aktualisieren könnten, die das Parsen zeigt, das auch für Funktionen funktioniert. (zB Funktion fff() {echo "fff";}; export -f fff) –

14

Anstatt dass Ihr Python-Skript das bash-Skript eingibt, wäre es einfacher und eleganter, eine Wrapper-Skriptquelle init_env zu haben und dann Ihr Python-Skript mit der modifizierten Umgebung auszuführen.

#!/bin/bash 
source init_env 
/run/python/script.py 
+8

Dies löst das Problem, aber beantwortet nicht die Frage :) – getekha

+0

Es kann lösen das Problem in einigen Umständen, aber nicht alle von ihnen. Zum Beispiel schreibe ich ein Python-Skript, das etwas tun muss, wie * die Datei zu beschaffen (es lädt tatsächlich Module, wenn Sie wissen, wovon ich rede), und es muss je nach Umständen ein * anderes * Modul laden. Also das würde mein Problem überhaupt nicht lösen – Davide

21

Mit Gurke:

import os, pickle 
# For clarity, I moved this string out of the command 
source = 'source init_env' 
dump = '/usr/bin/python -c "import os,pickle;print pickle.dumps(os.environ)"' 
penv = os.popen('%s && %s' %(source,dump)) 
env = pickle.loads(penv.read()) 
os.environ = env 

Aktualisiert:

Dies verwendet json, subprocess und explizit verwendet/bin/bash (für ubuntu-Unterstützung):

import os, subprocess as sp, json 
source = 'source init_env' 
dump = '/usr/bin/python -c "import os, json;print json.dumps(dict(os.environ))"' 
pipe = sp.Popen(['/bin/bash', '-c', '%s && %s' %(source,dump)], stdout=sp.PIPE) 
env = json.loads(pipe.stdout.read()) 
os.environ = env 
+5

das ist schlau. –

+0

einverstanden, das ist interessant! – Hamy

+0

Dieser hat ein Problem auf Ubuntu - die Standard-Shell ist '/ bin/dash', die den' source' Befehl nicht kennt. Um es unter Ubuntu zu verwenden, müssen Sie '/ bin/bash' explizit ausführen, z. mit 'penv = subprocess.Popen (['/ bin/bash', '-c', '% s &&% s'% (Quelle, Speicherauszug)], stdout = subprocess.PIPE) .stdout' (dies verwendet die neueres 'Subprocess'-Modul, das importiert werden muss. –

-8
execfile('filename') 

sollte tun Sie es

+5

Nein, sollte es wirklich nicht. Execfile analysiert 'Dateiname' als Python, was der Fragesteller nicht gefragt hat ... Wenn Sie eine 4 Jahre alte Frage beantworten, sollten Sie wirklich etwas Nützliches hinzufügen. – Foon

+0

Ah, ich habe seine Frage missverstanden. Ich lese es als "Quelle wie" -Funktion in Python. Ich habe heute nach dieser Funktion gesucht und mich gewundert, warum es nicht enthalten war - mein Schlechter. – rupert160

1

Die Antwort von @ lesmana auf Python 3 wurde aktualisiert. Beachten Sie die Verwendung von env -i, die verhindert, dass externe Umgebungsvariablen gesetzt/zurückgesetzt werden (möglicherweise fälschlicherweise aufgrund der fehlenden Handhabung für mehrzeilige env-Variablen).

import os, subprocess 
if os.path.isfile("init_env"): 
    command = 'env -i sh -c "source init_env && env"' 
    for line in subprocess.getoutput(command).split("\n"): 
     key, value = line.split("=") 
     os.environ[key]= value 
2

Beispiel @ Brians ausgezeichnete Antwort in einer Funktion Verpackung:

import json 
import subprocess 

# returns a dictionary of the environment variables resulting from sourcing a file 
def env_from_sourcing(file_to_source_path, include_unexported_variables=False): 
    source = '%ssource %s' % ("set -a && " if include_unexported_variables else "", file_to_source_path) 
    dump = '/usr/bin/python -c "import os, json; print json.dumps(dict(os.environ))"' 
    pipe = subprocess.Popen(['/bin/bash', '-c', '%s && %s' % (source, dump)], stdout=subprocess.PIPE) 
    return json.loads(pipe.stdout.read()) 

ich diese Nutzenfunktion bin mit aws Anmeldeinformationen und Andockfenster .env Dateien mit include_unexported_variables=True zu lesen.