파이썬 스크립트에서 쉘 명령어 사용

때로는 Python 코드를 짜는 거보단 Linux 명령어를 사용하면 편리하게 처리할 수 있는 것들이 있다. 그래서 파이썬 스크립트 내에서 쉘 명령어를 실행하는 방법을 알아보았다. 참고로 아래 내용은 Python 3.10에서 테스트한 코드이다. 


!command

주피터 노트북에서는 ! (느낌표)를 이용해 명령어를 실행할 수 있다. 

!ls

그런데 이 방법은 주피터 노트북(.ipynb)에서만 가능하다.


subprocess

subprocess 모듈에는 쉘 명령어를 실행할 수 있는 다양한 방법이 존재하지만 그 중 run 함수가 있다. 

import subprocess

# subprocess.run(명령어)
subprocess.run("ls")
subprocess.run("ls", cwd="./projectC")

문자열 형태로 명령어를 입력할 수 있다. cwd는 해당 명령어를 수행할 디렉토리 위치를 설정할 수 있으며, 생략하면 실행한 현재 디렉토리를 기준으로 처리된다. 

subprocess.run("echo TEST")
subprocess.run(["echo", "TEST"])

문자열리스트를 입력하는 방식 모두 동일한 결과를 출력한다. 

>>> ls_ = subprocess.run("ls", capture_output=True, encoding="utf-8")
>>> ls_
CompletedProcess(args='ls', returncode=0, stdout='...', stderr='')
>>> ls_.stdout
__pycache__
test.py
test.sh
...

capture_output을 True로 설정하면 실행의 결과값을  CompletedProcess 객체의 형태로 변수에 저장할 수 있다. 

encoding은 결과값의 인코딩 형식이며, 생략할 경우 바이너리 형식으로 반환한다. 

자세한 내용은 공식문서에서 확인할 수 있다. 

 

 

Command Injection

>>> import subprocess
>>> message = "'Hello' && ls"
>>> subprocess.run(f"echo {message}", shell=True)
'Hello' 
main.js   test.py
>>> subprocess.run(f"echo {message}", shell=False)
Hello && ls

shell 옵션을 True로 설정할 경우, command injection이 발생할 수 있다. 따라서, shell=False로 실행하는 것이 안전하다. 


os

os 모듈을 활용하는 방법도 있다. 

>>> import os 
>>> me = os.popen("whoami")
>>> me
<os._wrap_close object at 0x0000016D3B89AEC0>
>>> me.read()
abc\def

popen은 명령어를 실행한 후 os 객체를 반환한다. 이후 read 메서드를 통해 실행 결과를 확인할 수 있다. 

os.system("whoami")

단순히 명령을 출력하는 상황이라면 os.system을 사용하는 방법도 있다.