Python에서 Bash '소스'에뮬레이션


92

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

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.


답변:


109

접근 방식의 문제점은 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(). 나는 그것을 버그라고 생각하지만 나는 우회한다.


12
내 보내지 않은 변수에 관심이 있고 스크립트가 제어 범위를 벗어난 경우 set -a를 사용하여 모든 변수를 내 보낸 것으로 표시 할 수 있습니다. 명령을 다음과 같이 변경하십시오. [ 'bash', '-c', 'set -a && source init_env && env']
ahal

내 보낸 함수에서는 실패합니다. 함수에서도 작동하는 구문 분석을 보여주는 답변을 업데이트 할 수 있다면 좋겠습니다. (예 : function fff () {echo "fff";}; export -f fff)
DA

2
참고 : 여러 줄 환경 변수를 지원하지 않습니다.
BenC

2
내 경우에는, 반복 proc.stdout()수익률은 따라서 나는를 얻고 있었다, 바이트 TypeErrorline.partition(). line.decode().partition("=")문제 가 해결 된 문자열로 변환 .
Sam F

1
이것은 매우 도움이되었습니다. ['env', '-i', 'bash', '-c', 'source .bashrc && env']rc 파일에서 설정 한 환경 변수 만 제공하기 위해 실행 했습니다
xaviersjs

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, 하위 프로세스를 사용하고 명시 적으로 / 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

이것은 Ubuntu에서 문제가 있습니다. 기본 쉘 /bin/dashsource명령을 알지 못합니다 . Ubuntu에서 사용하려면 /bin/bash명시 적 으로 실행해야합니다 . 예를 들어 다음을 사용하여 실행해야합니다 penv = subprocess.Popen(['/bin/bash', '-c', '%s && %s' %(source,dump)], stdout=subprocess.PIPE).stdout(이는 subprocess가져와야 하는 최신 모듈을 사용함 ).
Martin Pecka

22

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

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

4
일부 상황에서는 문제가 해결 될 수 있지만 전부는 아닙니다. 예를 들어 , 파일 소싱 과 같은 작업을 수행해야하는 파이썬 스크립트를 작성 중이며 (실제로 내가 말하는 내용을 알고 있으면 모듈을로드합니다) 상황에 따라 다른 모듈 을로드해야합니다 . 그래서 이것은 내 문제를 전혀 해결하지 못할 것입니다
Davide

이것은 대부분의 경우 질문에 대한 답변이며 가능한 한 사용합니다. 주어진 프로젝트의 IDE에서이 작업을 수행하는 데 어려움을 겪었습니다. 한 가지 가능한 수정은 환경과 함께 셸에서 모든 것을 실행하는 것입니다bash --rcfile init_env -c ./script.py
xaviersjs

6

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

env -i가 경로를 설정 해제하기 때문에 이것을 사용하면 "PATH : 정의되지 않은 변수"가 제공됩니다. 그러나 env -i 없이는 작동합니다. 또한 라인이있을 수 있음을주의 여러 '='
후지이

5

함수에서 @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.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.