HOWTO · Python
Python에서 외부 프로그램 호출
이 튜토리얼은 Python에서 외부 프로그램을 실행하는 방법을 보여줍니다.
이 페이지의 내용
대부분의 일반적인 외부 명령에는 subprocess.run()을 사용하세요. 실행 파일과 그 인수를 목록으로 전달하면 Python이 셸에 해석을 맡기지 않습니다. 읽기 쉬운 출력이 필요하면 capture_output=True와 text=True를 설정한 다음, 완료된 프로세스 객체에서 stdout, stderr, returncode를 확인하세요. 아래 예제는 Python 자체를 시작하므로 셸 유틸리티가 설치되어 있는지에 의존하지 않습니다.
다음 예를 실행하세요:
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "print('Hello from Python')"],
capture_output=True,
text=True,
check=True,
)
print(result.stdout, end="")
print("Return code:", result.returncode)
Hello from Python
Return code: 0
목록을 사용하면 인수 내부의 공백을 포함해 각 인수가 분리된 상태로 유지됩니다. text=True는 플랫폼의 텍스트 인코딩을 사용해 캡처된 스트림을 문자열로 디코딩합니다. 이 옵션이 없으면 stdout과 stderr는 바이트입니다. check=True를 설정하면 0이 아닌 명령 상태는 subprocess.CalledProcessError를 발생시킵니다. 상태를 직접 처리하고 싶으면 이 옵션을 빼세요. 캡처된 출력은 명령의 상태와 별개입니다. 명령은 어느 스트림에든 쓸 수 있으며 여전히 0이 아닌 returncode로 종료할 수 있습니다.
셸 명령 실행에 os.system() 사용하기
os.system()은 문자열을 명령 프로세서(일반적인 플랫폼에서는 셸)를 통해 실행하고 명령 프로세서의 상태를 반환합니다. 명령의 표준 출력과 표준 오류는 보통 부모 프로세스에 연결된 상태로 유지되므로, 이 호출로는 캡처된 텍스트를 얻을 수 없습니다. 셸 동작이 의도된 경우에 단순한 셸 명령을 실행하기 위한 레거시 선택입니다. 인수 목록, 캡처된 스트림 또는 구조화된 상태 처리가 필요하면 subprocess.run()을 선호하세요.
입력은 셸 구문이므로, 신뢰할 수 없는 텍스트를 삽입해 문자열을 만들지 마세요. 셸 메타문자는 실행되는 내용을 바꿀 수 있습니다. 정확한 명령 프로세서와 상태 인코딩은 플랫폼마다 다르므로, 반환된 값이 항상 자식 프로그램의 수정되지 않은 종료 코드라고 가정하지 마세요. 예를 들어, 이식 가능한 개념적 호출은 os.system("some-command")입니다. 실행할 운영 체제에 맞는 구체적인 명령과 인용 규칙을 선택하세요.
명령 출력 읽기에 os.popen() 사용하기
os.popen()은 셸을 통해 실행되는 명령에 대한 파이프를 열고 파일과 유사한 객체를 반환합니다. 스트림을 읽고 다 읽으면 닫으세요. close()는 명령 상태도 제공할 수 있습니다. 이 인터페이스는 단순한 파이프 기반 상호 작용을 위한 레거시 옵션이며, subprocess에서 사용할 수 있는 stdout과 stderr의 분리 처리를 제공하지 않습니다.
예를 들어, 형태는 pipe = os.popen(command)이고, 그 뒤에 output = pipe.read()와 status = pipe.close()가 이어집니다. 명령 문자열은 셸이 해석하므로, 신뢰할 수 없는 입력에는 os.system()과 같은 주의를 기울이세요. close()의 반환값은 캡처된 출력을 대신하지 않으며, 그 상태 표현은 플랫폼에 따라 달라질 수 있습니다. 출력과 명확한 반환 코드가 모두 필요한 새 코드에는 subprocess.run()을 선호하세요.
저수준 프로세스 제어에 subprocess.Popen() 사용하기
subprocess.Popen()은 프로그램이 실행 중인 프로세스와 상호 작용해야 할 때 사용하는 클래스 생성자입니다. 직접 실행하려면 인수 목록을 전달하세요. stdout=subprocess.PIPE 또는 stderr=subprocess.PIPE는 해당 스트림을 읽을 계획이 있을 때만 설정하세요. 구성된 파이프를 수집하고 완료를 기다리려면 communicate()를 호출하고, 파이프 출력을 수집할 필요가 없으면 wait()를 호출하세요.
자식 프로세스가 다른 파이프에 많이 쓰는 동안 한 파이프를 수동으로 읽으면 운영 체제 버퍼가 가득 차서 프로세스가 멈출 수 있습니다. communicate()는 두 스트림을 함께 수집하는 일반적인 방법이며, 텍스트 모드가 활성화되지 않으면 바이트를 반환합니다. 출력이 크거나 스트리밍되는 경우, 모든 것을 메모리에 버퍼링하지 말고 스트림을 신중하게 처리하세요. 기다린 뒤 완료된 결과를 확인하기만 하면 되면 subprocess.run()이 더 간단합니다.
명령을 실행하고 기다리는 데 subprocess.call() 사용하기
subprocess.call()은 명령을 실행하고, 끝날 때까지 기다린 뒤 그 상태를 반환합니다. 셸 해석을 피하려면 subprocess.call(["program", "--version"])와 같은 목록을 제공하세요. 이 편의 함수는 기본적으로 출력을 캡처하지 않으며, 자식 프로세스의 스트림은 상속됩니다. 캡처된 스트림, 텍스트 디코딩 또는 CompletedProcess 필드가 필요하면 subprocess.run()을 사용하세요. call()은 기존 코드가 동기적인 상태만 필요할 때 여전히 유용하지만, 새 코드는 일반적으로 더 명확한 run() 인터페이스에서 이점을 얻습니다.
상태 0은 일반적으로 성공을 나타내고, 0이 아닌 상태는 명령이 문제를 보고했음을 알립니다. 특정 0이 아닌 값의 의미는 호출된 프로그램에 속하며, 상태 규칙은 플랫폼마다 다를 수 있습니다. 0이 아닌 상태를 오류로 취급해야 하면 subprocess.run(..., check=True)가 CalledProcessError를 발생시킵니다. 그렇지 않으면 returncode를 확인하고 응용 프로그램이 어떻게 대응할지 결정하세요.
대부분의 외부 명령에 subprocess.run() 사용하기
일반적인 작업에서는 subprocess.run()이 완료될 때까지 기다리고 CompletedProcess를 반환합니다. capture_output=True를 사용하면 결과에 stdout과 stderr가 들어 있고, text=True를 사용하면 이 값들은 문자열이 됩니다. 이 예제는 표준 출력과 반환 코드를 출력하며, 표준 오류는 result.stderr에서 계속 사용할 수 있습니다. 프로그램이 표준 오류에 출력을 쓰면 그 내용은 result.stdout에 나타나지 않습니다.
인수 목록을 선호하고, 프로그램을 직접 호출할 때는 기본값인 shell=False를 유지하세요. 이렇게 하면 인수 안의 셸 메타문자가 셸 구문으로 해석되는 것을 막을 수 있지만, 대상 프로그램 자체의 입력 및 옵션 처리 위험은 여전히 있습니다. shell=True는 파이프라인이나 리디렉션 같은 셸 기능이 필요할 때만 사용하고, 그 경우에는 셸 명령을 고정하거나 플랫폼에 맞는 신중한 인용으로 구성하세요. 인수 목록이 이식 가능한 기본값이지만, 실행 파일 이름과 명령줄 옵션은 운영 체제마다 여전히 다릅니다.
명령이 실패하면 그 결과를 어떻게 처리할지 선택하세요. check=True를 사용하면 CalledProcessError와 함께 중단되고, false로 두면 result.returncode를 기준으로 분기할 수 있습니다. 실행 파일이 없거나 프로세스를 시작할 수 없으면 완료된 결과가 존재하기 전에 운영 체제 오류가 발생합니다. 출력 텍스트만으로 성공을 추론하지 마세요. 상태를 사용하고, 관련이 있으면 두 스트림을 모두 살펴보세요.
결론
대부분의 외부 명령에는 subprocess.run()을 사용하세요. 인수를 분리된 상태로 유지하고, 프로세스를 기다리며, 상태와 캡처된 스트림이 들어 있는 결과를 제공합니다. 프로세스가 실행되는 동안 저수준 상호 작용이 필요하면 subprocess.Popen()을 사용하세요. subprocess.call()은 상태만 반환하는 작은 동기 편의 함수입니다. os.system()과 os.popen()은 셸 기반 레거시 인터페이스이므로, 그 동작이 작업에 맞을 때만 선택하세요. 셸 입력은 신뢰할 수 있는 것으로 유지하고, 필요한 스트림만 캡처하며, 실행 파일 이름과 상태 규칙이 플랫폼마다 다르다는 점을 고려하세요.