ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 파이썬으로 쉘 명령어 날리기 (부제: AWS CLI 에러 읽어오기)
    Tips 2022. 6. 16. 01:34
    728x90
    반응형

     

    Python으로 쉘 명령어를 날리는 법 중 세 가지를 정리해본다.

     

    1. os.system

    import os
    
    os.system('쉘 명령어')

    os.system은 쉘 명령어의 수행 결과값을 콘솔에 출력한다. 즉, 수행 결과값을 파이썬 내에서 사용할 수 없다.

    import os
    
    os.system('echo Hello')
    result = os.system('echo Hello')
    print(result)

    위의 파이썬 파일을 실행하면 출력값은 Hello/Hello/0이다. 즉, 변수에 담는 코드 줄도 결과를 콘솔에 출력하고 result에는 0이 담긴다.

     

    2. os.popen

    출력 결과값을 변수에 담아서 사용하고 싶다면 os.popen을 사용할 수 있다.

    import os
    
    os.popen('쉘 명령어')

    os.popen은 결과값을 콘솔에 출력하지 않고 수행만 한다 (수행은 한다는 점에 주의). 결과값을 변수에 담고싶다면 .read() 함수를 호출하면 된다.

    import os
    
    os.popen('echo Hello')
    obj = os.popen('echo Hello')
    print(result)
    result = os.popen('echo Hello').read()
    print(result)

    위의 파이썬 파일 출력값은 <os._wrap_close object at 0x1030d6fd0>/Hello다. 두 번의 print의 수행 결과값이다.

     

    문제는 여기서 봉착했다. 나는 이 기능을 AWS CLI 명령어를 전달하는데 사용했는데, os.popen은 명령어의 최종 결과값만 받아온다. 즉, 하위 프로세스에서 에러가 난 경우, 해당 에러메세지는 콘솔에 출력하고 결과로 받아오지 못했다. 그래서 사용한 모듈이 subprocess이다.

     

    3. subprocess.Popen

    (맨 앞에 P 대문자임에 주의)

    import subprocess
    
    subprocess.Popen('쉘 명령어')
    class subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, 
    preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=None,  
    startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False,
    pass_fds=(), *, encoding=None, errors=None, text=None)

    (공식문서)

    Popen은 option 설정으로 콘솔에 결과값을 출력할 수도, 변수에 담을 수도 있다. 몇 가지 옵션을 살펴보면 다음과 같다.

    • stdout: Captured stdout from the child process -> PIPE로 할당하면 subprocess를 실행시키는 표준 입출력으로 사용할 수 있다.
    • stderr: Captured stderr from the child process -> stdout으로 설정하면 출력으로 같이 볼 수 있다.
    • text: 출력값을 텍스트로 받을 건지 바이너리로 받을 건지 설정하는 인자이다 (아래 코드 예제에서 출력값을 비교해보자).
    • shell: shell을 통해 명령어 전달. 해당 인자를 True로 사용하지 않으면 명령어에 띄워쓰기가 들어가는 경우 리스트로 전달해야한다 (아니면 파일명으로 인식한다). 하지만 유효성 검사가 되지 않기 때문에 False로 지정하는 것이 권장된다. shell=True를 안 쓸 경우 명령어.split(' ')으로 념겨줘도 된다.
    import subprocess
    
    one_line = subprocess.Popen('ls', text=True, stdout=subprocess.PIPE).stdout.read()
    
    shell_false = subprocess.Popen(['echo','Hello'], text=True, stdout=subprocess.PIPE).stdout.read()
    use_split = subprocess.Popen('echo Hello'.split(), text=True, stdout=subprocess.PIPE).stdout.read()
    shell_true = subprocess.Popen('echo Hello', shell=True, text=True, stdout=subprocess.PIPE).stdout.read()
    
    text_false = subprocess.Popen('echo Hello', shell=True, stdout=subprocess.PIPE).stdout.read()
    
    print(one_line) #띄어쓰기가 없는 명령어는 shell=True 옵션이 없어도 잘 출력된다
    print(shell_false) #띄어쓰기가 있는 경우 리스트로 전달하거나
    print(use_split) #split()을 사용하거나
    print(shell_true) #shell=True 옵션을 사용한다.
    print(text_false) #text 옵션을 false로 지정하면 바이너리로 출력된다.

    위 파일의 출력값은 (파일 리스트)/Hello/Hello/Hello/b'Hello\n' 이다.

    그래서 다음 옵션을 통해 AWS CLI 명령어 에러 출력값을 텍스트로 변수에 담을 수 있었다.

    from subprocess import Popen, PIPE, STDOUT
    
    command = 'aws lambda update-function-code --function-name batch-test --zip-file fileb://lambda_function.zip'
    result = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True, text=True).stdout.read()

     

    정말 다행히도 모두 파이썬 표준 라이브러리이다. 표준 라이브러리 사랑해요

    그리고 마지막으로 저걸 다 찾아내고 나서야 그냥 boto3를 쓰면 된다는 것이 생각났다 ^_^v. 참고로 AWS에서도 파이썬에서 저런 방식으로 AWS CLI 명령어를 전달한는 것보다 boto3를 사용하는 것을 권장한다고 한다.

    728x90
    반응형

    댓글