프로그래밍 언어/Python

파이썬에서 외부 터미널 명령어 사용하기 ( subprocess )

JMDev 2023. 9. 19. 15:11

파이썬에서 subprocess 를 활용해 자식 프로세스를 실행하면서, 

자식 프로세스의 입출력 스트림을 관리 및 활용할 수 있습니다

import subprocess

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

# Hello from subprocess!

위 코드를 통해 subprocess에 터미널 명령을 넣어주면 아래와 같은 결과값을 얻을 수 있음을 확인할 수 있습니다.

 

만약 내가 터미널 명령을 통한 결과값을 이용해 파이프라인을 구축하고 싶다면 

Popen 클래스를 활용해 유닉스 스타일의 파이프라인을 구축해볼 수 있을 것 같은데요.

 

여기서 유닉스 스타일의 파이프라인이란 유닉스 계열 운영체제에서 제공하는 파이프라인( | ) 를 사용해서

여러 명령어 출력 및 입력을 연결하는 방식을 말합니다

 

import subprocess

# 'echo'의 출력을 'grep'의 입력으로 전달하는 파이프라인 예제
with subprocess.Popen(['echo', 'Hello from subprocess!'], stdout=subprocess.PIPE) as proc1:
    with subprocess.Popen(['grep', 'Hello'], stdin=proc1.stdout, stdout=subprocess.PIPE) as proc2:
        output = proc2.communicate()[0]
        print(output.decode('utf-8'))
        
 # Hello from subprocess!

위와 같이 파이프라인으로 표현된 명령어를 표현할 수 있을 것 입니다.

 

다른 예제로 확인해보면,

cat somefile.txt | grep "some pattern" | sort

위 명령어를 아래와 같은 방식으로 표현이 가능할 것 입니다.

import subprocess

# 첫 번째 명령어: cat somefile.txt
with subprocess.Popen(['cat', 'somefile.txt'], stdout=subprocess.PIPE) as proc1:
    # 두 번째 명령어: grep "some pattern"
    with subprocess.Popen(['grep', 'some pattern'], stdin=proc1.stdout, stdout=subprocess.PIPE) as proc2:
        # 세 번째 명령어: sort
        with subprocess.Popen(['sort'], stdin=proc2.stdout, stdout=subprocess.PIPE) as proc3:
            output = proc3.communicate()[0]
            print(output.decode('utf-8'))

만일, subprocess의 예상된 실행시간을 초과해버리면 어떻게 할까요?

그땐 commuicate의 파라미터 timeout를 활용해 실행결과 시간을 제한해

시간이 지나버리면 오류가 발생할 수 있게 유도할 수 있습니다.

import subprocess

with subprocess.Popen(['sleep', '10'], stdout=subprocess.PIPE) as proc:
    try:
        output = proc.communicate(timeout=5)[0]
    except subprocess.TimeoutExpired:
        proc.kill()
        output = proc.communicate()[0]
        print("Process timed out!")

 

마지막으로, subprocess가 병렬로 실행되고 있다는 것을 확인해볼 수 있는 코드를 보면서

이런 병렬성을 어떻게 활용할 수 있는지 까지 알아볼 수 있을 것 같습니다

import subprocess
import time

def run_command_in_parallel(commands):
    """여러 명령어를 병렬로 실행하고 결과를 반환합니다."""
    processes = []
    for cmd in commands:
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        processes.append(proc)

    results = []
    for proc in processes:
        stdout, stderr = proc.communicate()
        results.append((stdout, stderr))

    return results

def main():
    # 예제: 동시에 여러 'sleep' 명령어 실행
    commands = [
        ['sleep', '5'],
        ['sleep', '5'],
        ['sleep', '5']
    ]

    start_time = time.time()
    run_command_in_parallel(commands)
    end_time = time.time()

    elapsed_time = end_time - start_time
    print(f"Total time taken: {elapsed_time:.2f} seconds")

if __name__ == "__main__":
    main()
    
 # Total time taken: 5.01 seconds

마지막에 5초라는 시간이 결과값으로 나온 것을 확인함으로써 병렬로 실행되고 있음을 알 수 있습니다.

이러한 병렬이라는 특색을 이용해 아래와 같은 시스템을 구축하거나 활용해볼 수 있을 것 같습니다.

  1. 데이터 처리: 큰 데이터 세트를 여러 부분으로 나누고, 각 부분을 독립적으로 처리한 후 결과를 합치는 작업에 사용될 수 있습니다. 예를 들어, 로그 파일을 분석하거나 대규모 텍스트 데이터를 처리할 때 유용합니다.
  2. 병렬 컴파일: 큰 소프트웨어 프로젝트에서 여러 소스 파일을 동시에 컴파일하여 전체 빌드 시간을 단축시킬 수 있습니다.
  3. 시뮬레이션 및 모델링: 과학적 연구나 엔지니어링에서 복잡한 시뮬레이션을 여러 파라미터로 동시에 실행하여 결과를 빠르게 얻을 수 있습니다.
  4. 웹 크롤링: 여러 웹사이트나 페이지를 동시에 크롤링하여 데이터를 수집하는 데 사용될 수 있습니다.
  5. 배치 작업: 여러 작업을 동시에 실행하여 시스템의 처리 용량을 최대한 활용할 수 있습니다. 예를 들어, 여러 이미지나 동영상 파일을 동시에 변환하는 작업 등이 있습니다.
  6. 테스트 자동화: 여러 테스트 케이스나 시나리오를 동시에 실행하여 소프트웨어의 테스트 시간을 단축시킬 수 있습니다.
  7. 분산 시스템: 여러 노드나 서버에서 동시에 작업을 실행하여 분산 시스템의 성능을 테스트하거나 최적화할 수 있습니다.