본문 바로가기
Python

[Python] subprocess 모듈 사용법 및 예제

by A6K 2022. 7. 3.

subprocess는 파이썬 스크립트에서 쉘 명령 등 다른 프로세스를 실행하고 출력 결과를 가져올 수 있게 해주는 라이브러리다. subprocess 모듈은 os.system, os.spawn* 등을 대체하기 위해 만들어졌다.

subprocess 사용법

call() 함수

가장 기본적인 서브 프로세스 호출명령이다.

인자로 넘겨진 값들을 이용해 바로 프로세스를 실행한다. 프로세스를 실행하고, 실행한 프로세스의 종료를 기다렸다가 리턴코드를 반환한다. 프로세스가 정상종료했으면 0을 리턴하고, 비정상 종료라면 프로세스가 반환하는 에러 코드를 가져온다.

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
  • args : 쉘에 입력할 명령어 문자열을 공백 문자로 나눈 문자열 리스트다. 즉 실행할 명령어와 그 명령어를 위한 인자들이 문자열 리스트 형태로 표현된다.
  • stdin, stdout, stderr : 각각 실행될 프로세스의 표준 입력, 표준 출력, 표준 에러의 리다이렉션을 지정한다.
  • shell : 별도의 서브 쉘을 실행하고 그 위에서 명령을 실행할지 여부를 지정한다. 쉘을 이용하면 명령어 실행시 쉘이 제공하는 명령 파이프라이닝, 리다이렉션과 쉘의 문자열 확장(file* 같은)을 사용할 수 있다. shell=True로 사용하는 경우 args는 리스트 형태가 아닌 문자열 형태로 쓰는게 좋다.
  • timeout : call() 함수는 블러킹 함수다. timeout을 지정하면 일정 시간만 기다렸다가 TimeoutExpired 예외를 발생시킨다.

call() 함수를 사용한 예제는 다음과 같다.

>>> import subprocess
>>> subprocess.call(["ls", "-al"])
합계 0
drwxr-xr-x 2 user group 44   6월  26 11:53 .
drwxr-xr-x 8 user group 150  6월  26 11:53 ..
-rw-r--r-- 1 user group 0    6월  26 11:53 file1
-rw-r--r-- 1 user group 0    6월  26 11:53 file2
0
>>> subprocess.call("ls -al file* > out", shell=True)
0
>>> subprocess.call("ls -al", shell=True)
합계 4
drwxr-xr-x 2 user group 44   6월  26 11:53 .
drwxr-xr-x 8 user group 150  6월  26 11:53 ..
-rw-r--r-- 1 user group 0    6월  26 11:53 file1
-rw-r--r-- 1 user group 0    6월  26 11:53 file2
-rw-r--r-- 1 user group 104  6월  26 11:55 out

실행할 명령을 문자열 혹은 문자열 리스트로 입력해 실행시킬 수 있다. shell 인자를 True로 줄 경우 쉘에서 실행하는 형태로 명령어 문자열을 인자로 줘서 실행할 수 있다.

check_call()

check_call() 함수는 서브 프로세스에 의한 처리가 성공됨을 보장해야 하는 경우 쓰인다. 서브 프로세스의 실행이 비정상 종료되었다면 CalledProcessError 예외를 발생시킨다.

이 예외 객체에는 리턴 코드가 담겨 있으므로 왜 실패했는지 리턴코드를 확인해볼 수 있다.

check_output()

서브 프로세스를 실행하고 출력 문자열을 파이썬 로직에서 변수에 담아 사용하고 싶은 경우 사용한다. 함수 이름에 check_ 가 들어간 것에서 알 수 있듯이 비정상 종료되면 CalledProcessError 예외를 발생시킨다.

예를 들어 date 명령어로 시간 정보를 파이썬 로직으로 가져올 수 있다.

>>> import subprocess
>>> date_info=subprocess.check_output(["date"])
>>> print(date_info.decode("utf-8"))
2022. 07. 01. (금) 12:01:20 KST

date 명령을 실행한 다음 표준 출력으로 넘어온 데이터를 읽어서 리턴해준다. 리턴된 값은 date_info 같은 변수에 담아 사용할 수 있다.

stderr=subprocess.PIPE 인자를 넘겨주면 표준 에러로 출력된 내용을 받아오게 된다.

run()

사실 파이썬 3.5부터는 run() 함수가 추가되어 다른 함수들을 대체하게 되었다. 최신 버전의 파이썬3에서는 run() 함수하나만 써도 된다. 이전 버전들은 호환성을 위해 필요할 뿐이다.

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None, **other_popen_kwargs)

사용법과 인자의 의미는 call() 비슷하다.

  • args : 실행할 명령을 기술한다.
  • capture_output : 표준 출력과 표준 에러를 캡쳐한다. 명령을 실행하고 결과 값을 가져오는 check_output() 대신 쓸 수 있다.
  • check : 비정상 종료하는 경우 CalledProcessError를 발생시킬지 여부를 지정한다
  • input : 서브 프로세스의 표준 입력으로 전달할 데이터. 기본적으로 바이트스트림이지만 encoding= 옵션이나 text=True 로 전달하면 문자열이 전달된다.
  • env : 서브 프로세스의 환경 변수를 정의하는 매핑
  • 나머지는 call() 함수와 동일

run() 함수는 실행 결과로 CompletedProcess 객체를 리턴한다. CompletedProcess 객체는 다음 정보를 담고 있다.

  • args : 프로세스를 시작하는 데 사용된 인자 리스트 혹은 문자열
  • returncode : 서브 프로세스의 종료 코드
  • stdout : 서브 프로세스에서 캡쳐된 표준 출력 내용
  • stderr : 서브 프로세스에서 캡쳐된 표준 에러 내용
  • check_returncode() : 리턴코드가 0이 아니면 CalledProcessError를 발생시킴

shell=True로 설정했을 경우 입력받은 문자열을 쉘에서 바로 실행한다. 만약 사용자의 입력을 문자열 형태로 run()이나 call() 함수로 넘겨주는 경우, 명령어 인젝션에 대한 처리는 애플리케이션에서 해줘야한다.

Popen 클래스

Popen 클래스는 다양한 옵션을 통해 call(), check_output() 명령어보다 훨씬 유연하게 서브프로세스를 실행하고 결과값을 받아올 수 있게 해준다. 위에서 봤던 subprocess 함수들은 내부에서 Popen()을 사용한다.

subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

위에서 본 함수들은 모두 서브 프로세스가 종료될 때까지 기다리는 블로킹 함수들이었다. 이 함수들은 Popen을 이용해 구현되었다. 따라서 Popen을 이용하면 좀 더 유연하게 서브 프로세스들을 관리할 수 있다.

Popen 클래스는 컨텍스트 매니저 프로토콜을 구현하고 있다. 따라서 with 구문에 사용될 수 있다.

>>> with subprocess.Popen(["date"], stdout=subprocess.PIPE) as proc:
...     print(proc.stdout.read().decode("utf-8"))
...
2022. 07. 01. (금) 12:37:44 KST

Popen 클래스는 서브프로세스를 사용하기 위해 유용한 메소드들을 제공한다.

  • poll() : 서브 프로세스가 종료되었는지를 확인
  • wait(timeout=None) : 서브 프로세스가 종료되길 기다린 후 리턴코드를 가져옴
  • communicate(input=None, timeout=None) : 서브프로세스의 표준입력으로 input 데이터를 보낸 후 표준 출력에서 EOF를 만날 때까지(프로세스가 종료될때까지) 데이터를 읽어온다. (stdout_data, stderr_data) 튜플을 리턴한다. 이 함수를 사용하려면 stdin=subprocess.PIPE 옵션을 사용해야한다
  • Popen.send_signal(signal) : 서브 프로세스에 시그널을 보낸다
  • Popen.terminate() : 서브 프로세스에 종료 시그널을 보낸다
  • Popen.kill() : 서브 프로세스를 강제로 죽인다
  • Popen.pid : 서브 프로세스의 pid
  • Popen.returncode : 서브 프로세스가 종료되었을 때 리턴코드

Popen을 사용하는 예는 다음과 같다.

>>> proc = subprocess.Popen(['ls', '-al'], stdout=subprocess.PIPE, stderr=subprocess.PIPE )
>>> out = proc.communicate()
>>> out 
(b'total 8\\ndrwxr-xr-x. 2 root root 4096 10\\xec\\x9b\\x94  8 17:58 .\\ndrwxr-xr-x. 6 root root 4096 10\\xec\\x9b\\x94  8 17:58 ..\\n', b'')

 

파이썬 스크립트 작성에 도움되는 글 모음

파이썬으로 프로그램을 작성할 때 도움되는 글들을 모아본다. Python 문법 Python 모듈

hbase.tistory.com

 

댓글