Intro

파이썬 프로젝트 중 FFMPEG.exe 파일을 통해 음원파일을 인코딩해야 할 일이 생겼습니다.

자바에서는 ProcessBuilder 를 통해 외부 프로세스를 빌드하고, 커맨드를 실행시킬 수 있었는데요,

파이썬에서는 이와 같은 작업을 어떻게 할 수 있는지 한 번 알아보겠습니다.

참고 : subprocess.Popen() 메서드 사용을 추천함

os

os 라이브러리는 시스템과 관련된 여러 기능들을 포함하고 있습니다. 디렉토리, 파일에 대한 접근, 그리고 오늘 소개할 커맨드 실행 등의 기능도 가지고 있습니다.

os.system

os.system() 모듈은 현재 프로세스에서 command를 실행합니다.

EDI에서 코드를 실행시키면 command 창에서 그 결과를 받아볼 수 있고,
jupyter 환경에서 코드를 실행시킨다면 jupyter를 구동한 그 콘솔에서 결과를 받아볼 수 있습니다.

1
2
3
4
5
import os

result = os.system("ipconfig")
print(result)
>>> 0

command 실행 결과의 내용은 콘솔에서 볼 수 있으며, 파이썬 스크립트에서는 exit code만이 출력됩니다.

os.popen

os.popen() 모듈은 command를 실행하고 해당 command의 출력 내용을 파이썬에서 읽을 수 있는 값으로 반환하는 기능을 합니다.

하지만 os.popen()은 비권장 방식이며, 다음에 소개할 subprocess 모듈을 사용하는 것이 권장됩니다.

1
2
3
4
5
import os

result = os.popen("ipconfig")
output = result.read()
print(result)

subprocess

앞선 두 모듈의 보안적 취약성 때문에 subprocess 모듈의 사용이 권장됩니다.
현재 파이썬이 실행되는 프로세스가 아닌, 외부에 새로운 프로세스를 생성하고 관리하는 방식의 모듈입니다.

이 모듈을 통해 명령어 실행, 입출력 관리, 오류 처리를 더욱 안전하게 수행할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# subprocess.run()
import subprocess

## 변수 = subprocess.run(["command1", "command2" ...],
##                        stdin = 표준 입력 스트림,
##                        stdout = 표준 출력 스트림, 
##                        stderr = 표준 에러 스트림, 
##                        capture_output = True일 경우 실행 결과를 바이트 문자열로 반환,
##                        text = True일 경우 실행 결과를 텍스트 형식으로 반환,
##                        check = True일 경우 명령어 수행 실패시 CalledProcessError 예외,
##                        timeout = 시간제한 초. 시간내 미수행시 예외 발생)

result = subprocess.run(["ffmpeg/ffmpeg", "-i", "-y",
                         "test.wav", "-c:a", "pcm_s16le",
                         "-ar", "8000", "-ac", "1", "test_out.wav"],
                        capture_output=True,
                        text=True,
                        check=True)

print(result.stdout)

또한 Popen 메서드로 실행시 더욱 세부적인 제어를 할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# subprocess.Popen()
import subprocess

## 변수 = subprocess.Popen(["command1", "command2" ...],
##                         bufsize = 버퍼크기 지정. 기본 -1,
##                         executable = 실행 프로그램의 경로 지정,
##                         stdin = 표준 입력 스트림,
##                         stdout = 표준 출력 스트림, 
##                         stderr = 표준 에러 스트림, 
##                         preexec_fn = 자식 프로세스 실행 전 실행할 함수,
##                         close_fds = True시 부모프로세스에서 실행된 파일 디스크럽터를 자동으로 닫음,
##                         shell = Ture시 외부 명령어 실행시 셸을 사용. 기본False,
##                         cwd = 자식 프로세스가 실행될 현재 작업 디렉토리를 지정,
##                         env = 자식 프로세스가 사용할 환경 변수를 딕셔너리로 지정,
##                         ... 이외 여러 옵션 존재)

process = subprocess.Popen(["ffmpeg/ffmpeg", "-i", "-y",
                         "test.wav", "-c:a", "pcm_s16le",
                         "-ar", "8000", "-ac", "1", "test_out.wav"],
                         cwd=os.getcwd(),
                         stdout=subprocess.PIPE)

# 프로세스가 종료될 때까지 대기
subprocess.wait()

# 출력을 받음
output_text = process.stdout.read()

# 프로세스 종료
process.stdout.close()

print(output_text)

(ffmpeg 자체가 또다른 외부 프로세스여서 출력을 못받나..? 뭐지..?)

Reference

ChatGPT
https://codechacha.com/ko/python-run-shell-script/
https://docs.python.org/ko/3/library/os.html#process-parameters
https://docs.python.org/ko/3/library/subprocess.html#module-subprocess