HOWTO · Python

파이썬에서 배시 스크립트 실행하는 방법

Python을 사용하여 Bash 스크립트를 실행하는 방법을 우리의 종합 가이드를 통해 배우십시오. subprocess, os.system 및 subprocess.Popen과 같은 방법을 탐색하여 Bash 명령을 효율적으로 실행하십시오. Bash 스크립트를 Python 애플리케이션에 통합하여 자동화 기술을 향상시키고 워크플로를 간소화하십시오.

이 페이지의 내용

Python이 계속 실행되기 전에 끝나야 하는 대부분의 Bash 스크립트에는 subprocess.run을 사용하세요. 인자를 각각 전달하고 stdout과 stderr를 캡처하며 프로세스의 returncode를 확인할 수 있습니다. Bash가 설치되어 있고 Python에서 사용할 수 있어야 하므로, 이 방법은 호스트 환경에 따라 달라지며 Bash가 없는 시스템에서는 이식성이 없습니다. 아래 예제는 스크립트 파일을 직접 만들고 이름을 인자로 전달한 뒤 캡처한 결과를 표시합니다.

방법 1: subprocess 모듈 사용

이 독립 실행형 예제는 간단한 Bash 스크립트를 만들고, 인자를 전달한 뒤 두 출력 스트림을 모두 캡처합니다. Bash 스크립트는 인사말을 출력하고, Python은 캡처된 텍스트와 프로세스 상태를 출력합니다. 코드와 출력 블록은 변경되지 않으며, 블록에 표시된 레이블은 영어로 남아 있습니다.

import subprocess
import tempfile
from pathlib import Path

with tempfile.TemporaryDirectory() as directory:
    script_path = Path(directory) / "greet.sh"
    script_path.write_text(
        '#!/usr/bin/env bash\nprintf "Hello, %s!\\n" "$1"\n',
        encoding="utf-8",
    )

    result = subprocess.run(
        ["bash", str(script_path), "Ada"],
        capture_output=True,
        text=True,
        check=False,
    )

print(result.stdout, end="")
print(f"Return code: {result.returncode}")
Hello, Ada!
Return code: 0

인자 목록에서 스크립트 경로와 Ada는 별개의 인자입니다. 기본값인 shell=False에서는 Python이 셸에 목록 해석을 맡기지 않고 Bash를 직접 시작합니다. 따라서 해당 인자에는 셸 해석이 적용되지 않지만, 이것이 모든 상황에서 안전하다는 뜻은 아닙니다. 입력값을 검증하고 스크립트 자체가 어떤 동작을 하는지도 살펴보세요. returncode가 0이면 성공을 나타내고, 0이 아닌 값이면 프로세스가 실패를 보고한 것입니다. stderr는 진단 메시지를 위한 별도 스트림이며, 여기에 텍스트가 있다는 사실만으로 명령이 실패했다고 볼 수는 없습니다. capture_output=True를 지정하면 결과 객체에서 stdout과 stderr를 모두 확인할 수 있습니다.

방법 2: os.system 사용

간단한 명령이면 충분하고 Python에서 출력을 캡처할 필요가 없을 때만 os.system을 사용하세요. 이 함수는 하나의 명령 문자열을 받아 시스템 셸을 통해 실행하고, 명령의 스트림을 이를 둘러싼 프로세스의 표준 스트림으로 보냅니다. 반환값은 시스템에 따라 다르며 자식 프로세스의 종료 상태를 인코딩할 수도 있으므로, 이 값을 이식 가능한 직접 종료 코드로 취급하지 마세요. 출력 스트림을 담은 결과 객체도 직접 제공하지 않습니다. 셸이 입력을 특별한 의미로 해석할 수 있으므로 신뢰할 수 없는 입력을 이어 붙여 명령 문자열을 만들지 마세요. 인자를 따로 전달하거나 스트림을 캡처하고 상태를 신뢰성 있게 확인하려면 subprocess를 사용하세요.

방법 3: subprocess.Popen 사용

프로세스의 수명을 관리하거나 파이프를 연결하는 등 실행 중인 프로세스를 더 세밀하게 제어해야 한다면 subprocess.Popen을 선택하세요. 입력과 출력이 정해진 단순한 교환에서는 프로세스를 만들 때 stdin=subprocess.PIPE를 설정한 다음 communicate(input=...)를 호출해 입력을 보내고 프로세스가 끝난 뒤 캡처된 스트림을 모읍니다. 이 stdin 파이프가 없으면 communicate(input=...)으로 자식 프로세스에 데이터를 보낼 수 없습니다. 이 방식은 출력을 실시간으로 전달하지 않습니다. 두 출력 스트림 모두에 파이프를 만들었다면 communicate()로 읽어 비워 주세요. 파이프가 가득 찬 상태에서 자식 프로세스가 끝나기를 기다리면 교착 상태가 생길 수 있습니다. 실시간 스트리밍이 필요하다면 communicate()를 스트리밍 API처럼 취급하지 말고 스트림을 직접 읽어 비워야 합니다. 정확한 파이프 동작은 플랫폼과 프로세스에 따라 달라집니다.

결론

끝까지 실행되는 Bash 스크립트에는 보통 subprocess.run이 가장 명확한 선택입니다. 인자 목록을 전달하고, 필요하면 스트림을 캡처하며, returncode로 성공 여부를 판단하세요. 출력 처리가 필요 없는 간단한 셸 명령에는 os.system을, 명시적인 프로세스 제어나 통신이 필요할 때는 subprocess.Popen을 사용하세요. Python 프로그램이 실행되는 환경에 Bash가 설치되어 있고 사용할 수 있는지 확인하세요. Python이 Bash 실행 파일을 찾을 수 있어야 스크립트를 시작할 수 있습니다.

FAQ

  1. Python에서 Bash 스크립트를 실행하는 가장 좋은 방법은 무엇인가요?
    Python이 계속 실행되기 전에 끝나는 대부분의 스크립트에는 subprocess.run을 사용하세요. 인자 목록, 캡처된 스트림, returncode를 통한 프로세스 상태 확인을 지원합니다.

  2. 스크립트 출력을 어떻게 캡처하나요?
    subprocess.run에 capture_output=True와 text=True를 지정한 뒤 반환된 객체에서 stdout과 stderr를 읽으세요. Popen에서는 필요한 파이프를 연결하고 communicate()를 호출해 프로세스가 끝난 뒤 스트림을 모을 수 있습니다. 이 방식은 출력을 실시간으로 스트리밍하지 않습니다. 프로세스가 성공해도 stderr에 진단 메시지가 있을 수 있으므로 returncode로 상태를 확인하세요.

  3. 인자 목록을 전달하면 안전한가요?
    shell=False에서는 목록으로 전달한 인자를 셸이 해석하지 않습니다. 셸 인젝션 위험을 줄여 주지만 모든 스크립트나 입력이 안전해지는 것은 아닙니다. 값을 검증하고 스크립트가 수행하는 동작도 고려하세요.

  4. Bash 스크립트에 인자를 전달할 수 있나요?
    네. subprocess 인자 목록에서 스크립트 경로 뒤에 각 인자를 별도 항목으로 추가하세요. os.system은 명령 문자열 하나를 받으므로 같은 방식의 구조화된 인자 전달을 제공하지 않습니다.

  5. 스크립트에 입력을 전달하려면 어떻게 하나요?
    정해진 입력과 출력이 오가는 간단한 경우에는 subprocess.run에 input=...을 사용하세요. 더 세밀한 프로세스 제어가 필요하면 Popen에 stdin=subprocess.PIPE를 설정하고 communicate(input=...)을 사용하세요. communicate()는 입력을 보내고 자식 프로세스가 끝난 뒤 스트림을 모읍니다.