본문 바로가기
목차
Python

[Py] subprocess 모듈 사용법.

by ds31x 2025. 6. 7.
728x90
반응형

https://earthly.dev/blog/python-subprocess/

 

이 문서는 subprocess Module 의 HighLevel Methods의 사용법을 다룸:

  • subprocess.run(),
  • subprocess.getoutput(),
  • subprocess.check_output(),
  • subprocess.Popen
Python 3.5 이상을 기준으로 작성됨.

1. subprocess 모듈이란?

subprocess 모듈은 Python에서

  • 외부 명령어(예: 셸 명령, 다른 프로그램 실행)를 실행하고
  • 그 입력/출력/에러를 관리하는 데 사용됨.

2. 사전 준비

  • Python 버전: Python 3.5 이상 (subprocess.run()은 3.5부터 도입).
  • 모듈: subprocess는 표준 라이브러리에 포함되어 별도 설치 불필요.
  • 플랫폼: Windows, macOS, Linux에서 동작하는 예제 포함.
import subprocess

3. 하이레벨 메서드별 사용법 및 반환값

3-1 subprocess.run(): 동기식 실행 (blocking)

  • subprocess.run()
    • 명령어를 실행하고 완료될 때까지 기다린 후 결과를 반환하는
    • 동기식 메서드임.
  • 가장 유연하고 현대적인 하이레벨 API.

3-1-1. 기본 사용법

result = subprocess.run(
               ['echo', 'Hello, World!'], 
               capture_output=True, 
               text=True,
         )

print(result.stdout)  # 출력: Hello, World!

3-1-2. 설명:

  • 인자:
    • 명령어와 인자를 리스트 형태로 전달 (예: ['echo', 'Hello, World!']).
  • capture_output=True:
    • 자식 프로세스의 표준 출력(stdout)과 표준 에러(stderr)를 캡처.
    • 이는 stdout=subprocess.PIPEstderr=subprocess.PIPE 와 같은 동작이며 run()에서만 지원: Popen에선 stdoutstderrsubprocess.PIPE 를 지정해주는 방법만 지원함.
    • 자식 프로세스 의 표준 출력을 반환되는 객체의 .stdout 으로 확인하려면, stdout=subprocess.PIPE 를 지정한다.
    • 만약 자식 프로세스의 표준에러를 표준출력으로 연결하고, 표준출력으로 합쳐서 부모프로세스에서 확인하면, stdout=subprocess.PIPEsdterr=subprocess.STDOUT 을 해주면 됨.
  • text=True:
    • 출력을 문자열(str)로 반환
    • Python 3.7+ 에서 지원
    • 이전 버전에서는 universal_newlines=True 로 같은 효과 가능.
  • shell=True:
    • shell 에서 명령어가 실행됨.
    • 인자를 포함한 하나의 문자열로 처리 가능해짐.
    • 보안 주의.
  • check=True:
    • 명령어가 실패하면 CalledProcessError 예외 발생.
  • timeout:
    • 실행 시간 제한
    • second 단위 (예: timeout=5)

3-1-3. 반환값 타입

  • 반환 객체:
    • subprocess.CompletedProcess
  • 속성:
    • returncode: 실행 결과 코드 (int, 0은 성공, 그 외는 실패).
    • stdout: 표준 출력 (str if text=True, else bytes or None).
    • stderr: 표준 에러 (str if text=True, else bytes or None).

3-1-4. 예제: 디렉토리 목록 출력

result = subprocess.run(
            ['dir'], 
            capture_output=True, 
            text=True, 
            shell=True,
        )

print(result.stdout)  # 출력: 디렉토리 목록 (문자열)
print(type(result))    
print(type(result.stdout))
  • Windows에서는 dir,
  • Linux/macOS에서는 ls 사용.

3-1-5. 예제: 에러 처리

try:
    result = subprocess.run(
        ['ls', 'non_existent_dir'], 
        capture_output=True, 
        text=True, 
        check=True,
    )
except subprocess.CalledProcessError as e:
    print(f"에러: {e.stderr}")  # stderr는 문자열
    print(type(e.stderr))

3-1-6. 예제: 입력 전달

result = subprocess.run(
        ['grep', 'test'], 
        input='test line\nother line', 
        capture_output=True, 
        text=True,
    )

print(result.stdout)        # 출력: test line (문자열)
print(type(result.stdout))  #

3-2. subprocess.getoutput(): 무조건 str 반환

subprocess.getoutput()

  • 명령어를 shell 에서 실행 하고 ( run() 에서 shell=True로 설정한 것과 같음)
  • 표준 출력(stdout)과 표준 에러(stderr)를 무조건 문자열 str로만 반환.

3-2-1. 기본 사용법

output = subprocess.getoutput('echo Hello, World!')
print(output)  # 출력: Hello, World!
print(type(output))  #

3-2-2. 설명:

  • shell 실행: 내부적으로 shell=True로 실행.
  • 제한:
    • stderr는 캡처되지 않으며,
    • 실패 시 예외를 발생시키지 않음.
  • 보안 주의:
    • 사용자 입력을 직접 전달하면 shell injection 으로 인한 보안 위험성 존재.

3-2-3. 반환값 타입:

  • 반환: str (표준 출력 stdoutstderr는 포함 안 됨).

3-2-4. 예제: 시스템 정보 확인

output = subprocess.getoutput('uname -a')
print(output)  # 출력: 시스템 정보 (문자열)
print(type(output))  #

3-3. subprocess.check_output(): 출력 문자열 반환

subprocess.check_output()

  • stdout으로 출력된 표준출력만 str 객체로 반환** 하며,
  • stderr로 출력된 내용은 부모 프로세스의 stderr로 전달 (터미널 출력).
  • 명령어 실패 시 예외를 발생시킴 (check).

3-3-1. 기본 사용법

output = subprocess.check_output(
            ['echo', 'Hello, World!'], 
            text=True,
        )

print(output)  # 출력: Hello, World!
print(type(output))  # str

3-3-2. 설명:

  • text=True: 출력을 문자열 str로 반환 (아니면 bytes).
  • 예외: 명령어가 실패하면 CalledProcessError 발생.
  • 제한: stderr는 캡처되지 않음.
    • stderr는 터미널에 그대로 출력됨 (부모 프로세스의 stderr로 전달)

3-3-3. 반환값 타입:

  • 반환: str (if text=True, else bytes).
  • 예외: CalledProcessError (실패 시 stderr 접근 가능).

3-3-4. 예제: check_output으로 CalledProcessError 확인

try:
    output = subprocess.check_output(
                ['ls', 'not_exist_file'], 
                text=True,
            )
    print(output)
    print(type(output))  # 
except subprocess.CalledProcessError as e:
    print(f"CalleProcessError : {e.returncode = } / {e.cmd = }")  
    # stderr는 호출시킨 프로세스의 stderr stream으로 연결이 기본. 
    # check_output 함수의 stderr파라메터로 할당해서 별도 처리 필요.

3-4. subprocess.Popen: 비동기 실행

subprocess.Popen

  • 명령어를 비동기적으로 실행하며,
  • 실시간 출력 처리나 복잡한 프로세스 관리에 적합.

3-4-1. 기본 사용법

process = subprocess.Popen(
            ['ping', 'google.com'], 
            stdout=subprocess.PIPE, 
            text=True,
            )

stdout, stderr = process.communicate()

print(stdout)        # 출력: ping 결과 (문자열)
print(type(stdout))  #

3-4-2. 설명:

  • subprocess.PIPE:
    • 표준출력스트림/표준입력스트림 을 pipe로 연결.
  • communicate():
    • 프로세스 완료까지 대기하고
    • (stdout, stderr) 튜플 반환.
  • 비동기 로 동작됨:
    • 실행 즉시 제어가 반환되며,
    • communicate() 또는 wait()로 완료 대기 가능.
      • communicate() 사용을 권장.
      • wait()의 경우, subprocess.PIPE와 같이 사용한 경우 deadlock 위험성 존재.
      • 둘 다 timeout 지원: seconds 단위로 소수점 입력가능, 타임아웃시 subprocess.TimeoutExpired 발생.

3-4-3. 반환값 타입

  • 반환 객체:
    • subprocess.Popen
  • 속성/메서드:
    • returncode: 실행 후 반환 코드 (int, 초기화 전 None).
    • communicate():
      • (stdout, stderr) 튜플 반환
      • str if text=True, else bytes or None.
    • poll():
      • 프로세스 상태 확인,
      • 실행 중이면 None 반환.
      • 종료 시 반환 코드 (int ).

3-4-4. 예제: 실시간 출력 읽기

process = subprocess.Popen(
            ['ping', 'google.com'], 
            stdout=subprocess.PIPE, 
            text=True,
        )

while True:
    output = process.stdout.readline()

    if output == '' and process.poll() is not None:
        break

    if output:
        print(output.strip())  # 한 줄씩 문자열 출력

print(type(output))  #

3-4-5. 예제 : poll 사용법

import subprocess
import time

p = subprocess.Popen(["sleep", "2"])

while True:
    ret = p.poll()
    if ret is None:
        print("아직 실행 중...")
    else:
        print(f"종료됨. 반환 코드: {ret}")
        break
    time.sleep(0.5)

보다 자세한 것은 [[subprocess.Popen 사용하기]] 를 참조할 것.


4. 메서드 비교

Method Sync
여부
Return Type Desc.
subprocess.run() 동기 CompletedProcess
(with stdout, stderr: str or bytes)
유연, 현대적,
stdout/stderr 캡처 가능
subprocess.getoutput() 동기 str stdout만 반환,
셸 실행, 간단
subprocess.check_output() 동기 str or bytes stdout만 반환,
실패 시 예외 발생
subprocess.Popen 비동기 Popen 객체,
이후 communicate()로 
(str, str)or(bytes, bytes)
실시간 처리,
복잡한 관리 가능

5. 실제 활용 사례

5-1. 파일 목록 처리

result = subprocess.run(
            ['ls', '-l'], 
            capture_output=True, 
            text=True,
            )

files = result.stdout.splitlines()  # 문자열을 줄 단위로 분리

for file in files:
    print(f"파일: {file}")

print(type(result.stdout))  #

5-2.  외부 프로그램 실행

result = subprocess.run(
            ['python', '--version'], 
            capture_output=True, 
            text=True,
            )

print(result.stdout)  # 출력: Python 버전 (문자열)
print(type(result.stdout))  

5-3. 파이프라인

ps = subprocess.Popen(
            ['ps', 'aux'], 
            stdout=subprocess.PIPE, 
            text=True,
            )

grep = subprocess.Popen(
            ['grep', 'python'], 
            stdin=ps.stdout, 
            stdout=subprocess.PIPE, 
            text=True,
            )

output, _ = grep.communicate()

print(output)  # 출력: python 프로세스 정보 (문자열)
print(type(output))  # str

6. 주의사항

6-1. shell injection 주의:

shell=True 또는 getoutput() 사용 시 사용자 입력을 직접 전달하면 shell injection 위험성 존재함:

user_input = "malicious; rm -rf /"
subprocess.getoutput(f"echo {user_input}")

안전한 방법은 다음과 같음:

subprocess.run(
        ['echo', user_input], 
        capture_output=True, 
        text=True,
        )

6-2. 플랫폼 호환성:

import platform

cmd = ['dir'] if platform.system() == 'Windows' else ['ls']

result = subprocess.run(
            cmd, 
            capture_output=True, 
            text=True,
        )
print(result.stdout)  # 플랫폼에 따라 문자열 출력

6-3. str 권장:

text=True를 사용하지 않으면 bytes 반환:

result = subprocess.run(
            ['echo', 'test'], 
            capture_output=True,
            )  # text=True 없음
print(type(result.stdout))  # bytes

6-4. 예외 처리:

  • CalledProcessError: check=True 또는 check_output()에서 실패 시 raise.
  • TimeoutExpired: timeout 초과 시.

7. 기타

7-1. 비동기 작업:

asyncio.create_subprocess_execasync/await 지원.

import asyncio
async def run_cmd():
    proc = await asyncio.create_subprocess_exec(
                'ls', 
                stdout=asyncio.subprocess.PIPE,
        )
    stdout, _ = await proc.communicate()
    print(stdout.decode())  # 문자열로 변환

asyncio.run(run_cmd())

7-2. Environment Variables Setting:

result = subprocess.run(
    ['printenv', 'MY_VAR'], 
    env={'MY_VAR': 'value'}, 
    capture_output=True, 
    text=True,
)
print(result.stdout)  # 출력: value (문자열)

8. 공식 문서:

https://docs.python.org/3/library/subprocess.html

 

subprocess — Subprocess management

Source code: Lib/subprocess.py The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace seve...

docs.python.org

 


9. 결론

  • subprocess.run():
    • 동기식
    • 유연하고 가장 현대적인 방법으로 권장됨 (동기식으로 동작하는 경우)
    • CompletedProcess 객체 반환
    • Attribute인 stdout, stderrstr or bytes.
  • subprocess.getoutput():
    • 동기식
    • 간단한 str 출력, shell 실행.
  • subprocess.check_output():
    • 동기식
    • str 출력,
    • 실패 시 예외.
  • subprocess.Popen:
    • 비동기식
    • 비동기 처리 및 실시간 모니터링 등에 사용됨.
    • communicate(), wait() 등으로 동기식 처리 가능.
728x90