[python] Python에서 Bash ‘소스’에뮬레이션

다음과 같은 스크립트가 있습니다.

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

빌드 할 때마다 ‘source init_env'(init_env는 위의 스크립트 임)를 실행하여 일부 변수를 설정합니다.

Python에서 동일한 작업을 수행하기 위해이 코드를 실행했습니다.

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"

분명히 내 Python 스크립트가 무너졌습니다. 이 줄을 처리하도록 Python 스크립트를 수정할 수 있지만 나중에 누군가init_env파일 에서 사용할 새로운 기능을 제시 하면 나중에 중단 됩니다.

문제는 Bash 명령을 실행하는 쉬운 방법이 있는지 여부입니다 os.environ.



답변

접근 방식의 문제점은 bash 스크립트를 해석하려고한다는 것입니다. 먼저 export 문을 해석하려고합니다. 그런 다음 사람들이 변수 확장을 사용하고 있음을 알 수 있습니다. 나중에 사람들은 조건문을 파일에 넣거나 대체를 처리합니다. 결국 당신은 엄청난 버그를 가진 완전한 bash 스크립트 인터프리터를 갖게 될 것입니다. 그러지 마.

Bash가 파일을 해석하도록 한 다음 결과를 수집합니다.

다음과 같이 할 수 있습니다.

#! /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 자체가 실행되지 않거나 하위 프로세스가 bash를 실행하지 못하는 경우 또는 기타 오류 가 발생하는 경우 오류를 처리해야 합니다.

env -i명령 행의 시작 부분에 깨끗한 환경을 만듭니다. 즉,에서 환경 변수 만 가져옵니다 init_env. 상속 된 시스템 환경을 원하면 env -i.

자세한 내용 은 하위 프로세스에 대한 설명서 를 참조하십시오.

참고 : 내 보낸 변수 만 인쇄 하므로 export명령문으로 설정된 변수 만 캡처 env합니다.

즐겨.

있습니다 파이썬 문서는 당신이 환경을 조작 할 경우 조작하는 것을 말한다 os.environ사용하는 대신 직접 os.putenv(). 나는 그것을 버그라고 생각하지만 나는 우회한다.


답변

피클 사용 :

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, 하위 프로세스를 사용하고 명시 적으로 / bin / bash를 사용합니다 (우분투 지원을 위해).

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


답변

Python 스크립트가 bash 스크립트를 소스로 사용하는 것보다 래퍼 스크립트 소스를 init_env보유한 다음 수정 된 환경에서 Python 스크립트를 실행 하는 것이 더 간단하고 우아 합니다.

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


답변

Python 3에 대한 @lesmana의 답변을 업데이트했습니다. 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


답변

함수에서 @Brian의 훌륭한 대답을 감싸는 예 :

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

이 유틸리티 함수를 사용하여 .NET을 사용하여 aws 자격 증명 및 docker .env 파일을 읽고 있습니다 include_unexported_variables=True.


답변