파이썬 subprocess 환경변수 안 될 때: env·cwd·shell 확인 순서

터미널에서는 되던 명령이 파이썬 subprocess에서는 환경변수나 모듈을 찾지 못할 때가 있습니다. 먼저 부모 환경을 상속했는지 확인하고, env 매핑과 cwd를 분리해 살펴본 뒤 셸 초기화가 필요한지 판단하면 됩니다. shell=True부터 추가하기보다는 어떤 실행 환경이 달라졌는지 좁히는 편이 안전합니다.

부모 환경과 env 매핑부터 확인하기

env를 생략하면 자식 프로세스는 현재 환경을 상속하지만, env를 지정하면 그 매핑이 자식의 환경으로 사용됩니다. 따라서 필요한 항목 하나만 담은 env를 넘기면 기존 PATH 등도 빠질 수 있습니다. 부모의 os.environ은 현재 파이썬 프로세스의 환경변수 매핑입니다. 부모에 필요한 값이 있는지 먼저 확인하고, 일부만 바꿀 때는 복사본에 값을 더해 전달하세요. 원본을 바꾸지 않으므로 다른 자식 명령에 설정이 뜻밖에 퍼지는 일도 줄일 수 있습니다.

import os
import subprocess
import sys

child_env = os.environ.copy()
child_env["APP_MODE"] = "batch"

result = subprocess.run(
    [sys.executable, "-c", "import os; print('APP_MODE set:', bool(os.getenv('APP_MODE')))"],
    env=child_env,
    capture_output=True,
    text=True,
    check=False,
)
print("return code:", result.returncode)
if result.returncode != 0:
    print("child failed; inspect stderr securely")

이 예제는 설정값 자체를 출력하지 않습니다. 실패하면 returncode와 캡처한 stderr를 확인하되, 오류 메시지에도 경로나 비밀값이 담길 수 있으므로 공유하거나 로그에 남기기 전에 민감한 내용을 제거하세요. APP_MODE는 예시 이름이므로 실제 명령이 요구하는 키와 값으로 바꾸면 됩니다.

PATH에 폴더를 추가해야 한다면 child_env["PATH"] = 추가할_폴더 + os.pathsep + child_env.get("PATH", "")처럼 기존 값을 보존할 수 있습니다. 다만 아래 운영체제별 실행 파일 탐색 차이를 함께 확인해야 합니다. 부모에서부터 값이 없다면 os.getenv()가 None일 때 확인할 사항도 다음 질문에 도움이 됩니다.

파일 경로와 환경변수 매핑은 다릅니다

env는 변수 이름과 값을 담은 매핑이지 .env나 셸 설정 파일의 경로를 해석하는 옵션이 아닙니다. 파일에 저장된 설정이 필요하다면 적절한 방법으로 내용을 읽어 실제 키와 값으로 만든 뒤 자식에게 전달해야 합니다. 셸 설정 파일도 경로를 적었다는 이유만으로 실행되지는 않습니다. 반대로 자식 프로세스 안에서 바꾼 환경변수는 부모 파이썬의 환경으로 자동으로 돌아오지 않습니다. 자식이 계산한 값을 부모가 써야 한다면 표준 출력 등 명시적인 전달 경로를 설계해야 합니다.

cwd와 실행 파일 탐색을 나누어 보기

cwd는 자식 프로세스를 실행하기 전에 작업 디렉터리를 바꿉니다. 명령이 상대 경로의 입력 파일이나 설정 파일을 찾는다면 의도한 작업 폴더를 cwd로 지정하거나 파일의 절대 경로를 사용하세요. 이것은 환경변수를 추가하는 것과 별개의 조치입니다.

실행 파일 자체를 찾는 문제라면 운영체제 차이도 중요합니다. POSIX에서는 상대 실행 파일 탐색에 cwd와 전달한 환경의 PATH가 영향을 줄 수 있습니다. Windows에서 shell=False일 때는 cwd가 실행 파일 탐색 위치를 바꾸지 않고, 전달한 env의 PATH로 그 탐색을 덮어쓸 수 없습니다. 이 차이를 피하려면 실행 파일의 절대 경로를 지정하는 편이 명확합니다. 현재 파이썬 환경의 모듈을 실행할 때는 sys.executable과 -m을 사용해 어떤 인터프리터를 실행할지 분명히 할 수 있습니다.

셸 초기화가 필요한지 마지막으로 판단하기

Bash는 대화형 로그인 셸, 대화형 비로그인 셸, 비대화형 셸에서 읽는 시작 파일이 다릅니다. 따라서 터미널에서만 보이는 설정이 자동화 명령에도 있으리라고 가정하지 마세요. 필요한 값이 몇 개라면 부모에서 확인해 env로 명시하는 편이 추적하기 쉽습니다. 셸 초기화 자체가 필요한 경우에는 사용하는 셸과 시작 방식에 맞는 규칙을 확인해야 합니다.

shell의 기본값은 False이며, shell=True는 명령을 셸을 통해 실행합니다. 파이프나 셸 확장처럼 셸 기능이 실제로 필요할 때만 선택하고, 외부 입력을 셸 명령 문자열에 무심코 넣지 않도록 주의하세요. 환경변수 누락을 고치기 위한 만능 스위치로 취급하면 원인을 놓치기 쉽습니다.

결국 확인할 질문은 부모에 값이 있는지, 자식에게 어떤 매핑과 작업 폴더를 넘겼는지, 그리고 실행 파일과 셸이 무엇인지입니다. 실행 시간이 길어지는 별도 문제가 있다면 subprocess timeout 처리와 정리를 이어서 살펴보세요.

참고 자료

Leave a Comment