Programing

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

crosscheck 2020. 10. 10. 09:38
반응형

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

Updated @lesmana's answer for Python 3. Notice the use of env -i which prevents extraneous environment variables from being set/reset (potentially incorrectly given the lack of handling for multiline env variables).

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

Example wrapping @Brian's excellent answer in a function:

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

I'm using this utility function to read aws credentials and docker .env files with include_unexported_variables=True.

참고URL : https://stackoverflow.com/questions/3503719/emulating-bash-source-in-python

반응형