Python의 하위 프로세스는 무엇입니까? [5 Usage Examples]
하위 프로세스 활용법: 파이썬으로 운영 체제와 상호작용하기
운영 체제와의 상호 작용은 하위 프로세스를 통해 완전히 새로운 방식으로 가능해집니다.
컴퓨터는 항상 다양한 하위 프로세스를 작동시키고 있습니다. 이 글을 읽는 동안에도 네트워크 관리자나 웹 브라우저와 같은 여러 프로세스가 동시에 실행되고 있습니다.
흥미로운 점은 우리가 컴퓨터에서 수행하는 모든 작업이 하위 프로세스 호출과 관련되어 있다는 것입니다. 간단한 "hello world" 파이썬 스크립트를 실행할 때도 마찬가지입니다.
프로그래밍 경험이 있더라도 하위 프로세스 개념은 다소 모호하게 느껴질 수 있습니다. 이 글에서는 하위 프로세스의 핵심 개념과 파이썬 subprocess 표준 라이브러리를 사용하여 이를 구현하는 방법에 대해 자세히 알아보겠습니다.
이 튜토리얼을 마치면 다음을 할 수 있게 됩니다.
- 하위 프로세스의 기본 개념을 이해합니다.
- 파이썬 subprocess 라이브러리의 기본 사용법을 숙지합니다.
- 다양한 실용적인 예제를 통해 파이썬 기술을 향상시킵니다.
자, 시작해볼까요!
하위 프로세스란 무엇인가?
간단히 말해, 하위 프로세스는 다른 프로세스에 의해 생성된 컴퓨터 프로세스를 의미합니다.
하위 프로세스는 트리 구조로 생각할 수 있으며, 각 상위 프로세스는 자체 하위 프로세스를 실행합니다. 다소 복잡하게 느껴질 수 있지만, 간단한 그림을 통해 살펴보겠습니다.
컴퓨터에서 실행 중인 프로세스를 시각화하는 방법은 여러 가지가 있습니다. 예를 들어, 유닉스(리눅스 및 macOS)에서는 htop과 같은 대화형 프로세스 뷰어를 사용할 수 있습니다.

트리 모드는 실행 중인 하위 프로세스를 확인하는 데 매우 유용한 도구입니다. F5 키를 눌러 활성화할 수 있습니다.
명령 섹션을 살펴보면 컴퓨터에서 실행되는 프로세스 구조를 이해할 수 있습니다.

모든 것은 /sbin/init 명령에서 시작됩니다. 이 명령은 컴퓨터의 모든 프로세스를 시작하는 역할을 합니다. 그 이후부터는 xfce4-screenshoter나 xfce4-terminal과 같은 다른 프로세스들이 시작되는 것을 볼 수 있습니다. 물론 이러한 프로세스들도 더 많은 하위 프로세스를 생성합니다.
윈도우에서는 작업 관리자를 통해 프로세스를 확인할 수 있으며, 시스템에서 오류를 일으키는 프로그램을 강제 종료할 때 유용합니다.

이제 하위 프로세스에 대한 명확한 이해를 얻었으므로, 파이썬에서 이를 어떻게 구현하는지 살펴보겠습니다.
파이썬에서 하위 프로세스 사용하기
파이썬에서 하위 프로세스란 파이썬 스크립트가 운영 체제(OS)에 위임하는 작업을 의미합니다.
subprocess 라이브러리를 사용하면 파이썬에서 직접 하위 프로세스를 실행하고 관리할 수 있습니다. 여기에는 표준 입력(stdin), 표준 출력(stdout) 및 반환 코드 처리 작업이 포함됩니다.
subprocess는 파이썬 표준 라이브러리의 일부이므로 PIP를 사용하여 별도로 설치할 필요가 없습니다. 표준 라이브러리에 포함되어 있습니다.
따라서 `import subprocess` 구문만으로 파이썬에서 하위 프로세스를 사용할 수 있습니다.
import subprocess # 모듈 사용 예시...
참고: 이 글의 내용을 따라 하려면 파이썬 3.5 이상 버전이 필요합니다.
현재 파이썬 버전을 확인하려면 다음 명령을 실행하세요.
❯ python --version Python 3.9.5 # 예시
파이썬 2.x 버전을 사용하고 있다면 다음 명령을 대신 사용하세요.
python3 --version
계속해서 살펴보면, subprocess 라이브러리의 핵심 아이디어는 파이썬 인터프리터에서 직접 원하는 명령어를 실행하여 OS와 상호 작용할 수 있다는 것입니다.
즉, OS가 허용하는 범위 내에서 (루트 파일 시스템을 제거하지 않는 한 😅) 원하는 작업을 수행할 수 있습니다.
현재 디렉토리의 파일 목록을 출력하는 간단한 스크립트를 만들어 사용법을 알아보겠습니다.
첫 번째 하위 프로세스 애플리케이션
먼저 `list_dir.py` 파일을 만들어 봅시다. 이 파일은 우리가 파일 목록을 실험하는 데 사용할 것입니다.
touch list_dir.py
이제 이 파일을 열고 다음 코드를 입력합니다.
import subprocess
subprocess.run('ls')
먼저 `subprocess` 모듈을 가져온 다음, `subprocess.run()` 함수를 사용하여 인수로 전달하는 명령어를 실행합니다.
이 함수는 파이썬 3.5 버전에서 도입되었으며, 이전의 `subprocess.Popen` 함수를 더 쉽게 사용할 수 있도록 만든 바로 가기입니다. `subprocess.run()` 함수를 사용하면 명령어를 실행하고 완료될 때까지 기다릴 수 있습니다. 반면 `Popen`은 통신을 위해 추가적인 호출이 필요합니다.
코드 출력에 대해 이야기하자면, `ls`는 현재 디렉토리의 파일을 나열하는 유닉스 명령어입니다. 따라서 이 명령어를 실행하면 현재 디렉토리에 있는 파일 목록이 출력됩니다.
❯ python list_dir.py example.py LICENSE list_dir.py README.md
참고: 윈도우를 사용 중이라면 `ls` 대신 `dir`과 같은 다른 명령어를 사용해야 합니다.
이것은 매우 단순해 보일 수 있지만, 사실입니다. 우리는 쉘이 제공하는 모든 기능을 완전히 활용하고 싶습니다. 이제 `subprocess`를 사용하여 쉘에 인수를 전달하는 방법을 알아보겠습니다.
예를 들어, 숨겨진 파일(점으로 시작하는 파일)도 나열하고 파일의 모든 메타데이터를 출력하려면 다음 코드를 작성합니다.
import subprocess
# subprocess.run('ls') # 간단한 명령어
subprocess.run('ls -la', shell=True)
이 명령어는 문자열로 실행되며 `shell=True` 인수를 사용합니다. 즉, 하위 프로세스 실행 시 쉘이 호출되고, 명령 인수가 쉘에 의해 직접 해석됩니다.
하지만 `shell=True` 사용에는 보안 위험을 포함한 여러 단점이 있습니다. 자세한 내용은 공식 문서에서 확인할 수 있습니다.
`subprocess.run()` 함수에 명령어를 전달하는 가장 좋은 방법은 리스트를 사용하는 것입니다. 리스트의 첫 번째 요소(`[0]`, 여기서는 `ls`)는 실행할 명령어가 되고, 나머지 요소는 해당 명령어의 인수가 됩니다.
이 방법을 사용하면 코드는 다음과 같이 변경됩니다.
import subprocess
# subprocess.run('ls') # 간단한 명령어
# subprocess.run('ls -la', shell=True) # 위험한 명령어
subprocess.run(['ls', '-la'])
하위 프로세스의 표준 출력을 변수에 저장하려면 `capture_output=True` 인수를 설정하면 됩니다.
list_of_files = subprocess.run(['ls', '-la'], capture_output=True) print(list_of_files.stdout) ❯ python list_dir.py b'total 36ndrwxr-xr-x 3 daniel daniel 4096 may 20 21:08 .ndrwx------ 30 daniel daniel 4096 may 20 18:03 ..n-rw-r--r-- 1 daniel daniel 55 may 20 20:18 example.pyndrwxr-xr-x 8 daniel daniel 4096 may 20 17:31 .gitn-rw-r--r-- 1 daniel daniel 2160 may 17 22:23 .gitignoren-rw-r--r-- 1 daniel daniel 271 may 20 19:53 internet_checker.pyn-rw-r--r-- 1 daniel daniel 1076 may 17 22:23 LICENSEn-rw-r--r-- 1 daniel daniel 216 may 20 22:12 list_dir.pyn-rw-r--r-- 1 daniel daniel 22 may 17 22:23 README.mdn'
프로세스의 출력에 접근하려면 `stdout` 인스턴스 속성을 사용합니다.
이 경우 출력을 바이트가 아닌 문자열로 저장하고 싶다면 `text=True` 인수를 설정하면 됩니다.
list_of_files = subprocess.run(['ls', '-la'], capture_output=True, text=True) print(list_of_files.stdout) ❯ python list_dir.py total 36 drwxr-xr-x 3 daniel daniel 4096 may 20 21:08 . drwx------ 30 daniel daniel 4096 may 20 18:03 .. -rw-r--r-- 1 daniel daniel 55 may 20 20:18 example.py drwxr-xr-x 8 daniel daniel 4096 may 20 17:31 .git -rw-r--r-- 1 daniel daniel 2160 may 17 22:23 .gitignore -rw-r--r-- 1 daniel daniel 271 may 20 19:53 internet_checker.py -rw-r--r-- 1 daniel daniel 1076 may 17 22:23 LICENSE -rw-r--r-- 1 daniel daniel 227 may 20 22:14 list_dir.py -rw-r--r-- 1 daniel daniel 22 may 17 22:23 README.md
훌륭합니다! 이제 `subprocess` 라이브러리의 기본 사항을 알았으니, 몇 가지 사용 예시를 살펴보겠습니다.
파이썬에서 하위 프로세스 사용 예시
이 섹션에서는 `subprocess` 라이브러리의 실제 사용 사례를 살펴보겠습니다. 모든 예제는 Github 저장소에서 확인할 수 있습니다.
프로그램 설치 여부 확인
이 라이브러리의 주요 용도 중 하나는 간단한 OS 작업을 수행하는 기능입니다.
예를 들어, 프로그램이 설치되어 있는지 확인하는 간단한 스크립트를 만들어 보겠습니다. 리눅스에서는 `which` 명령어를 사용하여 이를 수행할 수 있습니다.
'''subprocess를 이용한 프로그램 확인'''
import subprocess
program = 'git'
process = subprocess.run(['which', program], capture_output=True, text=True)
if process.returncode == 0:
print(f'"{program}" 프로그램이 설치되어 있습니다.')
print(f'바이너리 위치: {process.stdout}')
else:
print(f'죄송합니다. {program} 프로그램이 설치되어 있지 않습니다.')
print(process.stderr)
참고: 유닉스에서는 명령이 성공적으로 실행되면 상태 코드가 0이 됩니다. 그렇지 않으면 실행 중에 문제가 발생한 것입니다.
`shell=True` 인수를 사용하지 않으므로 사용자 입력을 안전하게 처리할 수 있습니다. 또한 정규식 패턴을 사용하여 입력된 프로그램 이름이 유효한지 확인할 수도 있습니다.
import subprocess
import re
programs = input('공백으로 구분하여 확인할 프로그램 목록을 입력하세요: ').split()
secure_pattern = '^[a-zA-Z0-9]+$'
for program in programs:
if not re.match(secure_pattern, program):
print("죄송합니다. 해당 프로그램은 확인할 수 없습니다.")
continue
process = subprocess.run(
['which', program], capture_output=True, text=True)
if process.returncode == 0:
print(f'"{program}" 프로그램이 설치되어 있습니다.')
print(f'바이너리 위치: {process.stdout}')
else:
print(f'죄송합니다. {program} 프로그램이 설치되어 있지 않습니다.')
print(process.stderr)
print('n')
이 예제에서는 사용자로부터 프로그램 이름을 입력받고, 정규식을 사용하여 프로그램 문자열에 문자와 숫자만 포함되어 있는지 확인합니다. 그런 다음 for 루프를 사용하여 각 프로그램의 존재 여부를 확인합니다.
파이썬으로 간단한 grep 구현
당신의 친구 Tom은 텍스트 파일과 각 패턴에 대해 일치하는 횟수를 가져오고 싶어하는 패턴 목록이 담긴 또 다른 큰 파일을 가지고 있습니다. 그는 모든 패턴에 대해 `grep` 명령어를 실행하는 데 몇 시간을 소비했습니다.
다행히도 당신은 파이썬으로 이 문제를 해결하는 방법을 알고 있으며, 그가 몇 초 안에 작업을 완료하도록 도울 것입니다.
import subprocess
patterns_file="patterns.txt"
readfile="romeo-full.txt"
with open(patterns_file, 'r') as f:
for pattern in f:
pattern = pattern.strip()
process = subprocess.run(
['grep', '-c', f'{pattern}', readfile], capture_output=True, text=True)
if int(process.stdout) == 0:
print(
f'"{pattern}" 패턴은 {readfile} 파일의 어떤 줄에도 매칭되지 않았습니다.')
continue
print(f'"{pattern}" 패턴은 {process.stdout.strip()}번 매칭되었습니다.')
여기서는 작업할 파일 이름에 대한 변수를 두 개 정의합니다. 그런 다음 모든 패턴이 포함된 파일을 열고 반복합니다. 다음으로 "-c" 플래그(카운트를 의미)와 함께 grep 명령을 실행하는 하위 프로세스를 호출하고 조건부로 일치 결과를 결정합니다.
이 파일을 실행하면 (다음에서 텍스트 파일을 다운로드할 수 있음을 기억하십시오. Github 저장소) 결과가 출력됩니다.
하위 프로세스를 이용한 virtualenv 설정
파이썬으로 할 수 있는 가장 멋진 일 중 하나는 프로세스를 자동화하는 것입니다. 이러한 종류의 스크립트를 사용하면 매주 몇 시간을 절약할 수 있습니다.
예를 들어 가상 환경을 생성하고, 현재 디렉토리에서 `requirements.txt` 파일을 찾아 모든 종속성을 설치하는 설정 스크립트를 만들 것입니다.
import subprocess
from pathlib import Path
VENV_NAME = '.venv'
REQUIREMENTS = 'requirements.txt'
process1 = subprocess.run(['which', 'python3'], capture_output=True, text=True)
if process1.returncode != 0:
raise OSError('죄송합니다. python3가 설치되어 있지 않습니다.')
python_bin = process1.stdout.strip()
print(f'파이썬 위치: {python_bin}')
process2 = subprocess.run('echo "$SHELL"', shell=True, capture_output=True, text=True)
shell_bin = process2.stdout.split('/')[-1]
create_venv = subprocess.run([python_bin, '-m', 'venv', VENV_NAME], check=True)
if create_venv.returncode == 0:
print(f'{VENV_NAME} 가상 환경이 생성되었습니다.')
pip_bin = f'{VENV_NAME}/bin/pip3'
if Path(REQUIREMENTS).exists():
print(f'"{REQUIREMENTS}" 파일이 발견되었습니다.')
print('필요한 패키지를 설치합니다.')
subprocess.run([pip_bin, 'install', '-r', REQUIREMENTS])
print('설치가 완료되었습니다! "source .venv/bin/activate" 명령으로 환경을 활성화하세요.')
else:
print("requirements가 지정되지 않았습니다...")
이 예제에서는 여러 프로세스를 사용하고 파이썬 스크립트에서 필요한 데이터를 구문 분석합니다. 또한 `pathlib` 라이브러리를 사용하여 `requirements.txt` 파일의 존재 여부를 확인합니다.
파이썬 파일을 실행하면 OS에서 일어나는 일에 대한 몇 가지 유용한 메시지를 받게 됩니다.
❯ python setup.py 파이썬 위치: /usr/bin/python3 .venv 가상 환경이 생성되었습니다. "requirements.txt" 파일이 발견되었습니다. 필요한 패키지를 설치합니다. Collecting asgiref==3.3.4 ....... 설치가 완료되었습니다! "source .venv/bin/activate" 명령으로 환경을 활성화하세요.
표준 출력을 변수로 리디렉션하지 않으므로 설치 과정에서 출력되는 내용을 확인할 수 있습니다.
다른 프로그래밍 언어 실행
파이썬을 사용하여 다른 프로그래밍 언어를 실행하고 해당 파일에서 출력을 얻을 수 있습니다. 하위 프로세스는 운영 체제와 직접 상호 작용하기 때문에 가능한 일입니다.
예를 들어, C++과 Java로 Hello World 프로그램을 만들어 보겠습니다. 다음 파일을 실행하려면 C++ 및 Java 컴파일러를 설치해야 합니다.
helloworld.cpp
#include <iostream>
int main(){
std::cout << "C++로 작성한 Hello World!" << std::endl;
return 0;
}
helloworld.java
class HelloWorld{
public static void main(String args[]){
System.out.println("Java로 작성한 Hello World!");
}
}
단순한 파이썬 코드보다 복잡해 보이지만, 테스트를 위한 예제일 뿐입니다.
디렉토리에 있는 모든 C++ 및 Java 파일을 실행하는 파이썬 스크립트를 만들겠습니다. 먼저 파일 확장자에 따라 파일 목록을 얻기 위해 glob 모듈을 사용합니다.
from glob import glob
# 각 확장자에 해당하는 파일 목록 가져오기
java_files = glob('*.java')
cpp_files = glob('*.cpp')
이제 하위 프로세스를 사용하여 각 유형의 파일을 실행할 수 있습니다.
for file in cpp_files:
process = subprocess.run(f'g++ {file} -o out; ./out', shell=True, capture_output=True, text=True)
output = process.stdout.strip() + ' (파이썬으로 실행)'
print(output)
for file in java_files:
without_ext = file.strip('.java')
process = subprocess.run(f'java {file}; java {without_ext}',shell=True, capture_output=True, text=True)
output = process.stdout.strip() + ' (파이썬으로 실행)'
print(output)
여기서 문자열 함수의 `strip()`을 사용하여 출력을 정리하고 원하는 부분만 가져옵니다.
참고: 큰 Java 또는 C++ 파일을 실행할 때는 메모리 누수가 발생할 수 있으므로 주의해야 합니다. 출력을 메모리에 로드하기 때문입니다.
외부 프로그램 열기
하위 프로세스를 통해 바이너리 위치를 호출하여 다른 프로그램을 실행할 수도 있습니다.
제가 선호하는 웹 브라우저인 Brave를 실행해 보겠습니다.
import subprocess
subprocess.run('brave')
이 명령어를 실행하면 브라우저 인스턴스가 열리거나, 이미 브라우저를 실행 중인 경우 새 탭이 열립니다.

플래그를 허용하는 다른 프로그램과 마찬가지로, 플래그를 사용하여 원하는 동작을 실행할 수 있습니다.
import subprocess subprocess.run(['brave', '--incognito'])

마무리
하위 프로세스는 다른 프로세스에 의해 생성된 컴퓨터 프로세스입니다. htop이나 작업 관리자와 같은 도구를 사용하여 컴퓨터에서 실행 중인 프로세스를 확인할 수 있습니다.
파이썬에는 하위 프로세스 작업을 위한 자체 라이브러리가 있습니다. `subprocess.run()` 함수는 하위 프로세스를 생성하고 관리할 수 있는 간단한 인터페이스를 제공합니다.
우리는 OS와 직접 상호 작용하기 때문에 모든 종류의 응용 프로그램을 만들 수 있습니다.
마지막으로, 배우는 가장 좋은 방법은 사용하고 싶은 것을 직접 만들어 보는 것임을 기억하세요.