Python

Shell 명령의 결과를 받아오고 싶을 때

둔진 2020. 5. 1. 09:38

  파이썬 코딩을 하다 보면 이미 필요한 기능이 리눅스 명령으로 존재할 때가 있습니다. 가끔은 리눅스 명령을 실행해서 결과를 가져오는 것이 편할 때가 있죠. 물론 이 방법은 해당 명령이 해당 시스템에 설치되어야 있어야 하기 때문에 이식성이 조금 떨어집니다. 하지만 적절히 필요한 곳에 쓴다면 개발 속도를 확 높여 줄 수 있습니다.

 

  예를 들어 어떤 텍스트 파일이 몇 줄인지 궁금할 때가 있습니다. 파일을 열고, 줄 별로 읽어서 카운트를 할 수도 있지만, 리눅스의 wc 명령을 간편하게 이용할 수도 있습니다. 속도도 매우 빠르고요.

import subprocess

result = subprocess.run(['wc', '-l', 'wiki+namu.raw.txt'], stdout=subprocess.PIPE)
result_as_string = result.stdout.decode('utf-8')

  subprocess 모듈의 run 메쏘드를 호출하면 명령이 실행되고, 출력 결과를 PIPE에 저장하게 합니다. 그리고 나중에 result.stdout으로 이 결과를 쏙 빼오는 거죠. 한 가지 주의할 절음 result.stdout이 str이 아니고 byte이기 때문에 unicode로 변환을 해주어야 합니다. 아마 99%의 분들이 utf-8을 시스템 사용하실 테니 위와 같이 변화해주면 되고요.

print(result_as_string)
 38976216 wiki+namu.raw.txt
 
int(result_as_string.strip().split(' ')[0])
 38976216

  결과가 " 38976216 wiki+namu.raw.txt"와 같이 나오기 때문에 첫번째 부분만 떼어내서 int로 변환해주면 끝!

  

  번외로, 텍스트 파일이 몇 줄인지 알아보는 기능을 파이썬으로 구현했을 때와 wc 명령을 이용했을 때 얼마나 걸리는지 알아볼까요?

import subprocess

def get_lines_using_python_1():
    return len(open('wiki+namu.raw.txt').read().splitlines())

def get_lines_using_python_2():
    n = 0
    for line in open('wiki+namu.raw.txt'):
        n += 1
    return n

def get_lines_using_wc():
    result = subprocess.run(['wc', '-l', 'wiki+namu.raw.txt'], stdout=subprocess.PIPE)
    result_as_string = result.stdout.decode('utf-8')

    return int(result_as_string.strip().split(' ')[0])

if __name__ == '__main__':
    import timeit

    print(timeit.timeit(lambda: get_lines_using_python_1(), number=3))
    print(timeit.timeit(lambda: get_lines_using_python_2(), number=3))
    print(timeit.timeit(lambda: get_lines_using_wc(), number=3))

  첫번째 방법은 파일을 메모리에 한 번에 읽어서 줄단위로 쪼개는 방법이고,

  두번째 방법은 한 줄씩 읽어서 줄 수를 세는 방법이고요.

  세번째는 wc를 사용한 방법입니다.  

  파이썬의 기본 benchmark 모듈일 timeit을 사용해서 3회 반복으로 측정해줍니다. 결과의 단위는 초(s)입니다.

598.211259088
82.95156907900002
11.21919652500003

  생각보다 차이가 많이 나는군요.

  엔지니어링의 또 하나의 묘미가 적절한 장소에 적절한 도구를 쓰는 것 아니겠습니까?

  우리 모두 TPO에 맞는 선택으로 코딩 멋쟁이가 돼보아요.