पायथन में बैश 'स्रोत' का अनुकरण


91

मेरे पास एक स्क्रिप्ट है जो कुछ इस तरह दिखती है:

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

हर बार जब मैं निर्माण करता हूं तो कुछ चर स्थापित करने के लिए मैं 'init_env' (जहां init_env उपरोक्त स्क्रिप्ट है) चलाता हूं।

पायथन में वही पूरा करने के लिए मेरे पास यह कोड चल रहा था,

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)

लेकिन तब किसी ने निर्णय लिया कि init_envफ़ाइल की तरह एक लाइन जोड़ना अच्छा होगा :

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

जाहिर है मेरी पायथन लिपि बिखर गई। मैं इस लाइन को संभालने के लिए पायथन स्क्रिप्ट को संशोधित कर सकता था, लेकिन तब यह बाद में टूट जाएगा जब कोई व्यक्तिinit_env फ़ाइल में उपयोग करने के लिए एक नई सुविधा के साथ आएगा ।

सवाल यह है कि क्या बैश कमांड को चलाने का एक आसान तरीका है और इसे मेरा संशोधित करना है os.environ?


संबंधित:
सबप्रोसेस

जवाबों:


109

आपके दृष्टिकोण के साथ समस्या यह है कि आप बैश स्क्रिप्ट की व्याख्या करने की कोशिश कर रहे हैं। पहले आप केवल निर्यात विवरण की व्याख्या करने का प्रयास करें। फिर आप ध्यान दें कि लोग परिवर्तनशील परिवर्तन का उपयोग कर रहे हैं। बाद में लोग अपनी फ़ाइलों में सशर्त डाल देंगे, या प्रतिस्थापन की प्रक्रिया करेंगे। अंत में आपके पास एक पूर्ण विकसित बैश स्क्रिप्ट दुभाषिया होगा जिसमें गजिलियन बग होंगे। ऐसा मत करो।

बैश को आपके लिए फ़ाइल की व्याख्या करने दें और फिर परिणाम एकत्र करें।

आप इसे इस तरह से कर सकते हैं:

#! /usr/bin/env python

import os
import pprint
import shlex
import subprocess

command = shlex.split("env -i 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))

सुनिश्चित करें कि यदि आप bash में विफल रहते हैं source init_env, या bash स्वयं को निष्पादित करने में विफल रहता है, या subprocess bash, या किसी अन्य त्रुटि को निष्पादित करने में विफल रहता है , तो आप त्रुटियों को संभालते हैं।

env -iकमांड लाइन की शुरुआत में एक स्वच्छ वातावरण पैदा करता है। इसका मतलब है कि आप केवल पर्यावरण चर प्राप्त करेंगे init_env। यदि आप विरासत में मिला सिस्टम वातावरण चाहते हैं तो छोड़ दें env -i

अधिक जानकारी के लिए उपप्रकार पर प्रलेखन पढ़ें ।

नोट: यह केवल exportस्टेटमेंट्स के साथ सेट किए गए वेरिएबल्स को कैप्चर करेगा , क्योंकि envकेवल एक्सपोर्ट किए गए वेरिएबल प्रिंट करता है।

का आनंद लें।

ध्यान दें कि पायथन प्रलेखन कहता है कि यदि आप पर्यावरण में हेरफेर करना चाहते हैं तो आपको os.environउपयोग करने के बजाय सीधे हेरफेर करना चाहिए os.putenv()। मैं एक बग पर विचार करता हूं, लेकिन मैं पचाता हूं।


12
यदि आप गैर-निर्यात किए गए चर की परवाह करते हैं और स्क्रिप्ट आपके नियंत्रण से बाहर है, तो आप निर्यात के रूप में सभी चर को चिह्नित करने के लिए सेट -ए का उपयोग कर सकते हैं। बस कमांड बदलें: ['bash', '-c', 'set -a && source init_env && env']
ahal

ध्यान दें कि यह निर्यात किए गए कार्यों में विफल हो जाएगा। अगर आपको फ़ंक्शंस के लिए भी काम करने वाले अपने जवाब को अपडेट करने में मदद मिलती है तो मुझे अच्छा लगेगा। (उदा। फ़ंक्शन fff () {echo "fff";}; -f fff)
DA

2
नोट: यह बहुस्तरीय पर्यावरण चर का समर्थन नहीं करता है।
16

2
मेरे मामले में, से अधिक पुनरावृत्ति proc.stdout()पैदावार बाइट्स, इस प्रकार मैं एक हो रही थी TypeErrorपर line.partition()line.decode().partition("=")समस्या को हल करने के लिए स्ट्रिंग में परिवर्तित करना ।
सैम एफ

1
यह सुपर सहायक था। मैं ['env', '-i', 'bash', '-c', 'source .bashrc && env']अपने आप को केवल
आरसी

32

अचार का उपयोग करना:

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

अपडेट किया गया:

यह json, subprocess और स्पष्ट रूप से / bin / bash (ubuntu समर्थन के लिए) का उपयोग करता है।

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

यह एक उबंटू पर एक समस्या है - वहाँ डिफ़ॉल्ट शेल है /bin/dash, जो sourceकमांड को नहीं जानता है । उबंटू पर इसका उपयोग करने के लिए, आपको /bin/bashस्पष्ट रूप से चलना होगा , उदाहरण के लिए penv = subprocess.Popen(['/bin/bash', '-c', '%s && %s' %(source,dump)], stdout=subprocess.PIPE).stdout(नए subprocessमॉड्यूल का उपयोग करता है जिसे आयात किया जाना है)।
मार्टिन पीका

22

अपने पाइथन स्क्रिप्ट स्रोत को बैश स्क्रिप्ट के बजाय, यह एक रैपर स्क्रिप्ट स्रोत के लिए सरल और अधिक सुरुचिपूर्ण होगा init_envऔर फिर संशोधित वातावरण के साथ अपनी पायथन स्क्रिप्ट को चलाएं।

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

4
यह कुछ परिस्थितियों में समस्या को हल कर सकता है, लेकिन उन सभी को नहीं। उदाहरण के लिए, मैं एक अजगर स्क्रिप्ट लिख रहा हूं, जिसमें फ़ाइल को सोर्स करने जैसा कुछ करने की ज़रूरत है (वास्तव में यह मॉड्यूल लोड करता है यदि आप जानते हैं कि मैं किस बारे में बात कर रहा हूं), और इसे कुछ परिस्थितियों के आधार पर एक अलग मॉड्यूल लोड करने की आवश्यकता है। इसलिए यह मेरी समस्या को हल नहीं करेगा
Davide

यह ज्यादातर मामलों में सवाल का जवाब देता है, और मैं जहां भी संभव होगा, इसका उपयोग करूंगा। किसी दिए गए प्रोजेक्ट के लिए मेरे आईडीई में यह काम करने में मुझे मुश्किल समय था। एक संभव संशोधन पर्यावरण के साथ एक खोल में पूरी चीज को चलाने के लिए हो सकता हैbash --rcfile init_env -c ./script.py
xaviersjs

6

पायथन के लिए अपडेट किए गए @ lesmana का उत्तर 3. नोटिस करें env -iजिसके उपयोग से बाहरी वातावरण चर सेट / रीसेट होने से रोकता है (संभावित रूप से गलत तरीके से मल्टीलाइन एन वी चर के लिए हैंडलिंग की कमी को देखते हुए)।

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

इसका उपयोग करने से मुझे "पथ: अपरिभाषित चर" मिलता है क्योंकि एनवी -आई मार्ग को अनसेट करता है। लेकिन यह एनवी -आई के बिना काम करता है। यह भी ध्यान रखें कि लाइन
फ़ूजी

4

एक समारोह में @ ब्रायन का शानदार जवाब रैपिंग:

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())

मैं इस उपयोगिता समारोह का उपयोग करने के लिए a क्रेडेंशियल और docker include_unexported_variables=True। पढ़ने के लिए फ़ाइलों के साथ ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.