HOWTO · Python

Python에서 Bash 명령을 실행하는 방법

이 튜토리얼은 Git 명령에 초점을 맞춰 Python에서 bash 명령을 실행하는 방법을 보여줍니다. subprocess와 os.system을 포함하여 명령을 효과적으로 실행하는 가장 좋은 방법을 배우세요. 실용적인 예제와 명확한 설명으로 워크플로를 개선하세요. 작업을 자동화하고 프로세스를 간소화하려는 개발자에게 완벽합니다.

이 페이지의 내용

Python은 디렉터리 만들기나 Git 사용 같은 작업을 위해 명령줄 프로그램과 셸 명령을 시작할 수 있습니다. 종료되는 명령의 경우 subprocess.run()이 보통 가장 좋은 출발점입니다. 출력과 오류를 캡처할 수 있고 반환 코드를 제공합니다. 캡처된 출력이 필요 없는 단순한 경우에는 os.system()을 사용하고, 실행 중인 프로세스와 상호 작용해야 할 때는 subprocess.Popen()을 선택하세요.

subprocess.run()으로 명령 실행하기

인수 시퀀스를 전달하여 실행 파일을 직접 호출합니다. 이렇게 하면 셸이 명령을 파싱하도록 요청하는 것을 피할 수 있습니다. 예제는 실행 중인 Python 인터프리터를 사용하므로 Git가 설치되어 있는지나 특정 셸에 의존하지 않습니다.

import subprocess
import sys

result = subprocess.run(
    [sys.executable, "-c", "print('Hello from Python')"],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)

print("stdout:", result.stdout)
print("stderr:", result.stderr)
print("return code:", result.returncode)
stdout: b'Hello from Python\n'
stderr: b''
return code: 0

여기서 stdout과 stderr는 바이트이고, returncode는 정수입니다. 반환 코드 0은 관례적으로 성공을 나타내며, 0이 아닌 값은 프로그램이 문제를 보고했음을 알립니다. 직접 확인할 수도 있고, check=True를 전달하여 0이 아닌 반환 코드에 대해 run()이 CalledProcessError를 발생시키도록 할 수도 있습니다. 예를 들어 Git를 실행하려면 ['git', 'status']와 같은 인수 목록을 전달합니다. 그 출력은 저장소에 따라 달라지므로 고정된 내용이 있다고 가정하지 말고 데이터로 취급해야 합니다.

프로그램 호출이 자동으로 Bash 명령이 되는 것은 아닙니다. 파이프, 리다이렉션, &&와 같은 셸 구문에는 셸이 필요합니다. shell=True로 설정하면 Python이 플랫폼 셸을 사용하도록 요청합니다. Bash를 보장하지는 않습니다. 신뢰할 수 없는 입력을 셸 명령에 삽입하는 것은 명령 주입을 가능하게 할 수 있으므로 피하세요.

os.system()으로 셸 명령 실행하기

os.system()은 출력을 캡처하거나 오류를 상세히 처리할 필요가 없을 때 단순한 명령에 대한 제한적인 선택지입니다. 예를 들어 os.system("mkdir reports")는 플랫폼 셸에 디렉터리 생성 명령을 실행하도록 요청합니다. 명령은 상속된 콘솔 스트림에 씁니다. 이 함수는 명령의 일반적인 출력이 아니라 상태 값을 반환합니다. 그 상태의 해석은 플랫폼에 따라 다를 수 있습니다. 캡처된 출력, 오류 또는 반환 코드를 일관되게 검사해야 할 때는 subprocess.run()을 선호하세요.

진행 중인 프로세스에 subprocess.Popen() 사용하기

프로그램이 종료되기 전에 입력이나 다른 상호 작용이 필요할 때 subprocess.Popen()을 사용합니다. 프로세스를 시작하고 코드가 작업할 수 있는 프로세스 객체를 제공합니다. 출력이 제한된 프로세스의 경우 stdout=subprocess.PIPE와 stderr=subprocess.PIPE를 설정한 다음 communicate()를 호출하여 해당 스트림을 읽고 완료를 기다립니다. 디코딩을 별도로 마련하지 않는 한 반환되는 스트림 값은 바이트입니다.

communicate()는 프로세스가 종료되고 그 캡처된 출력이 메모리에 들어갈 수 있을 때 유용합니다. 무제한 출력에 대한 스트리밍 방식은 아닙니다. communicate()가 완료되면 프로세스는 종료된 것이므로, 그 프로세스에 kill()이나 terminate()를 호출할 필요가 없습니다. 단순히 종료되는 일반적인 명령의 경우 run()이 더 단순한 인터페이스를 제공합니다.

올바른 방법 선택하기

대부분의 명령에는 subprocess.run()을 사용하세요. 인수 목록을 전달하고, 필요할 때 스트림을 캡처하고, returncode를 검사합니다. 짧은 셸 명령으로 충분하고 그 출력과 상세 상태가 중요하지 않을 때만 os.system()을 사용하세요. 프로그램이 실행 중인 프로세스를 관리하거나 그와 데이터를 교환해야 할 때는 Popen()을 사용하세요. 명령에 셸 구문이 필요할 때만 셸을 선택하고, 신뢰할 수 없는 값을 삽입된 셸 문자열에 넣지 마세요.