Python에서 명령줄 인수를 구문 분석하는 방법

파이썬에서 명령줄 인수를 처리하는 효과적인 방법

파이썬 스크립트를 실행할 때 명령줄 인수를 사용하는 방법을 배우고 싶으신가요? 파이썬은 sys, getopt, argparse 모듈을 통해 이러한 기능을 제공하며, 이를 통해 명령줄 인수를 구문 분석하고 스크립트의 동작을 사용자 정의할 수 있습니다.

파이썬에서 사용자 입력을 받는 일반적인 방법은 input() 함수를 사용하는 것이지만, 특정 경우에는 스크립트 실행 시점에 명령줄을 통해 인수를 전달해야 할 수 있습니다.

이 튜토리얼에서는 파이썬 스크립트를 명령줄에서 실행할 때 옵션과 인수를 어떻게 활용하는지 살펴보고, 파이썬 내장 모듈을 사용하여 이러한 옵션과 인수를 처리하는 방법을 알아보겠습니다.

이제 시작해 보겠습니다!

파이썬에서 sys.argv의 역할 이해

C 언어 프로그래밍 경험이 있다면 명령줄을 통해 프로그램에 인수를 전달하는 것이 얼마나 편리한지 잘 아실 겁니다. C에서는 main 함수를 다음과 같이 구성하여 이를 처리합니다.

#include<stdio.h>

int main(int argc, char **argv){
    // argc: 인수의 개수
    // argv: 인수 벡터
    
    // 인수 처리 로직

    return 0;
}

여기서 argc는 전달된 인수 개수를 나타내고, argv는 실제 인수를 담고 있는 배열입니다.

파이썬 스크립트를 명령줄 인수로 실행하기

파이썬에서는 python3 filename.py 명령을 사용하여 명령줄에서 스크립트를 실행할 수 있습니다. 이 때, 스크립트에 다양한 명령줄 인수를 추가로 전달할 수도 있습니다.

$ python3 filename.py arg1 arg2 ... argn

sys 모듈은 이러한 명령줄 인수에 접근하고 처리하는 데 필요한 기능을 제공합니다. 특히, sys.argv는 스크립트 실행 시 전달된 모든 명령줄 인수를 담고 있는 리스트입니다.

아래는 main.py 스크립트를 명령줄 인수를 사용하여 실행하는 예시입니다.

$ python3 main.py hello world python script

for 루프와 enumerate 함수를 함께 사용하여 인수를 쉽게 순회할 수 있습니다.

# main.py

import sys

for idx, arg in enumerate(sys.argv):
    print(f"arg{idx}: {arg}")
# 실행 결과
arg0: main.py
arg1: hello
arg2: world
arg3: python
arg4: script

결과를 보면 첫 번째 인수(인덱스 0)는 항상 실행되는 파이썬 파일의 이름이고, 이후의 인수는 인덱스 1부터 시작하는 것을 알 수 있습니다.

이것은 명령줄 인수를 받아 처리하는 기본적인 프로그램입니다. 그러나 몇 가지 개선해야 할 점이 있습니다.

  • 사용자가 어떤 인수를 전달해야 하는지 어떻게 알 수 있을까요?
  • 각 인수는 어떤 의미를 가질까요?

이러한 정보가 부족하면 사용하기 어려울 수 있습니다. 이 문제를 해결하기 위해 getopt 또는 argparse 모듈을 활용할 수 있습니다. 다음 섹션에서 이 모듈들을 사용하는 방법을 자세히 알아보겠습니다.

getopt 모듈을 이용한 파이썬 명령줄 인수 구문 분석

파이썬의 내장 모듈인 getopt를 사용하여 명령줄 인수를 구문 분석하는 방법을 살펴보겠습니다.

getopt 모듈에서 getopt 함수를 가져온 후, 구문 분석할 인수를 지정하고 스크립트 실행에 필요한 짧은 옵션과 긴 옵션을 설정할 수 있습니다. sys.argv에서 인덱스 1부터 시작하는 모든 인수를 구문 분석해야 하므로, 구문 분석할 범위는 sys.argv[1:]가 됩니다.

예를 들어, 메시지 문자열과 파일 이름을 인수로 받도록 설정해 보겠습니다. 짧은 옵션으로 mf를 사용하고, 긴 옵션으로 messagefile을 사용합니다.

특정 옵션에 인수가 필요한지 어떻게 알 수 있을까요?

  • 짧은 옵션의 경우, 옵션 이름 뒤에 콜론(:)을 추가하여 해당 옵션에 인수가 필요하도록 지정할 수 있습니다.
  • 긴 옵션의 경우, 옵션 이름 뒤에 등호(=)를 추가하여 해당 옵션에 인수가 필요하도록 지정할 수 있습니다. 이렇게 하면 각 옵션과 해당하는 인수를 캡처할 수 있습니다.

이를 적용하여 main.py 파일에 다음 코드를 추가합니다.

# main.py

import sys
from getopt import getopt

opts, args = getopt(sys.argv[1:],'m:f:',['message=','file='])

print(opts)
print(args)

여기서 opts 변수에는 옵션과 인수가 튜플 리스트 형태로 저장되고, 전달된 다른 위치 인수는 args 변수에 리스트 형태로 저장됩니다.

이제 메시지와 파일 이름을 전달하여 스크립트를 실행할 수 있으며, 짧은 옵션이나 긴 옵션을 사용할 수 있습니다.

긴 옵션을 사용하여 main.py를 실행하면 다음과 같은 결과를 얻을 수 있습니다.

$ python3 main.py --message hello --file somefile.txt

opts 변수에는 옵션과 인수가 튜플 형태로 저장되어 있고, 위치 인수가 없으므로 args는 빈 리스트입니다.

# 실행 결과
[('--message', 'hello'), ('--file', 'somefile.txt')]
[]

짧은 옵션을 사용하여 동일한 작업을 수행할 수도 있습니다.

$ python3 main.py -m hello -f somefile.txt
# 실행 결과
[('-m', 'hello'), ('-f', 'somefile.txt')]
[]

⚠️ 주의: 이 예시에서 -m 짧은 옵션은 파이썬 스크립트를 모듈로서 실행할 때 사용하는 -m 명령줄 플래그와 혼동하지 않도록 주의해야 합니다.

예를 들어, main.py를 실행할 때 python3 -m unittest main.py를 사용하면 unittest 모듈을 실행하게 됩니다.

우리가 전달하는 다른 위치 인수는 args 변수에 수집될 것이라고 언급했습니다. 예를 들어,

$ python3 main.py -m hello -f somefile.txt another_argument

args 리스트에는 위치 인수 another_argument가 포함됩니다.

# 실행 결과
[('-m', 'hello'), ('-f', 'somefile.txt')]
['another_argument']

opts는 튜플 리스트이므로, 루프를 사용하여 튜플을 언패킹하고 각 옵션에 해당하는 인수를 추출할 수 있습니다.

이 인수를 처리한 후 파일 이름과 메시지를 어떻게 사용할 수 있을까요? 파일 쓰기 모드로 열고, 메시지를 대문자로 변환하여 파일에 쓸 수 있습니다.

# main.py
import sys
from getopt import getopt

opts, args = getopt(sys.argv[1:],'m:f:',['message=','file='])

print(opts)
print(args)

for option, argument in opts:
    if option == '-m':
        message = argument
    if option == '-f':
        file = argument

with open(file,'w') as f:
    f.write(message.upper())

짧은 옵션과 명령줄 인수를 사용하여 main.py를 실행해 보겠습니다.

$ python3 main.py -m hello -f thisfile.txt
[('-m', 'hello'), ('-f', 'thisfile.txt')]
[]

main.py를 실행하면 현재 작업 디렉토리에 ‘thisfile.txt’ 파일이 생성됩니다. 이 파일에는 ‘hello’ 문자열이 대문자로 변환된 ‘HELLO’가 포함되어 있습니다.

$ ls
main.py thisfile.txt
$ cat thisfile.txt
HELLO

argparse 모듈을 이용한 명령줄 인수 구문 분석

파이썬 표준 라이브러리에 포함된 또 다른 유용한 모듈인 argparse는 명령줄 인수를 처리하고 명령줄 인터페이스를 구축하는 데 특화된 기능을 제공합니다.

명령줄 인수를 구문 분석하려면 argparse 모듈에서 ArgumentParser 클래스를 가져와야 합니다. ArgumentParser 객체인 arg_parser를 인스턴스화합니다.

from argparse import ArgumentParser

arg_parser = ArgumentParser()

이제 두 개의 명령줄 인수를 추가해 보겠습니다.

  • message: 메시지 문자열
  • file: 작업할 파일 이름

arg_parser 객체의 add_argument() 메서드를 호출하여 이 두 인수를 추가합니다. add_argument() 메서드 호출 시 인수에 대한 설명을 help 문자열로 설정할 수 있습니다.

arg_parser.add_argument('message', help='메시지 문자열')
arg_parser.add_argument('file', help='파일 이름')

지금까지 arg_parser를 인스턴스화하고 명령줄 인수를 추가했습니다. 프로그램이 명령줄에서 실행될 때, arg_parser 객체의 parse_args() 메서드를 사용하여 인수 값을 가져올 수 있습니다.

여기서 인수 네임스페이스를 args 변수에 저장합니다. 따라서 args.인수_이름을 사용하여 각 인수의 값을 가져올 수 있습니다.

인수 값을 가져온 후, 대소문자를 바꾼 메시지를 (swapcase() 메서드를 사용하여) 파일에 쓸 수 있습니다.

args = arg_parser.parse_args()

message = args.message
file = args.file

with open(file,'w') as f:
     f.write(message.swapcase())

위의 내용을 모두 합치면 main.py 파일은 다음과 같습니다.

# main.py

from argparse import ArgumentParser

arg_parser = ArgumentParser()
arg_parser.add_argument('message', help='메시지 문자열')
arg_parser.add_argument('file', help='파일 이름')

args = arg_parser.parse_args()
print(args)

message = args.message
file = args.file

with open(file,'w') as f:
     f.write(message.swapcase())

명령줄 인수 사용법 이해하기

main.py를 실행할 때 인수 사용법을 확인하려면 --help 긴 옵션을 사용할 수 있습니다.

$ python3 main.py --help
usage: main.py [-h] message file

positional arguments:
  message     메시지 문자열
  file        파일 이름

optional arguments:
  -h, --help  이 도움말 메시지를 표시하고 종료합니다

선택적 인수가 없으며, 메시지와 파일 모두 필수 위치 인수입니다. 짧은 옵션 -h를 사용할 수도 있습니다.

$ python3 main.py -h
usage: main.py [-h] message file

positional arguments:
  message     메시지 문자열
  file        파일 이름

optional arguments:
  -h, --help  이 도움말 메시지를 표시하고 종료합니다

보시다시피 두 인수는 기본적으로 위치 인수입니다. 따라서 이러한 인수 중 하나라도 전달하지 않으면 오류가 발생합니다.

예를 들어, 메시지 문자열에 대한 위치 인수를 전달했지만 파일 인수에 대한 값을 제공하지 않았습니다.

이 경우 파일 인수가 필요하다는 오류가 발생합니다.

$ python3 main.py Hello
usage: main.py [-h] message file
main.py: error: 다음 인수가 필요합니다: file

두 개의 위치 인수를 사용하여 main.py를 실행하면 네임스페이스 args에 인수 값이 저장되는 것을 확인할 수 있습니다.

$ python3 main.py Hello file1.txt
# 실행 결과
Namespace(file='file1.txt', message='Hello')

현재 작업 디렉토리의 내용을 확인하면 스크립트가 file1.txt 파일을 생성한 것을 알 수 있습니다.

$ ls
file1.txt main.py

원래 메시지 문자열은 ‘Hello’입니다. 대소문자를 바꾼 후 file1.txt 파일의 메시지 문자열은 ‘hELLO’입니다.

$ cat file1.txt
hELLO

명령줄 인수를 선택적으로 만드는 방법

이러한 명령줄 인수를 선택 사항으로 만들려면 인수 이름 앞에 --를 붙일 수 있습니다.

메시지와 파일 인수를 모두 선택 사항으로 만들기 위해 main.py를 수정해 보겠습니다.

# main.py

from argparse import ArgumentParser

arg_parser = ArgumentParser()
arg_parser.add_argument('--message', help='메시지 문자열')
arg_parser.add_argument('--file', help='파일 이름')

명령줄 인수는 모두 선택 사항이므로 이러한 인수에 대한 기본값을 설정할 수 있습니다.

if args.message and args.file:
    message = args.message
    file = args.file
else:
    message="Python3"
    file="myfile.txt"

이 시점에서 main.py 파일은 다음 코드를 포함합니다.

# main.py

from argparse import ArgumentParser

arg_parser = ArgumentParser()
arg_parser.add_argument('--message', help='메시지 문자열')
arg_parser.add_argument('--file', help='파일 이름')

args = arg_parser.parse_args()
print(args)

if args.message and args.file:
    message = args.message
    file = args.file
else:
    message="Python3"
    file="myfile.txt"

with open(file,'w') as f:
     f.write(message.swapcase())

사용법을 확인하면 메시지와 파일이 모두 선택적 인수가 된 것을 알 수 있습니다. 이제 이 두 인수 없이 main.py를 실행할 수 있다는 의미입니다.

$ python3 main.py --help
usage: main.py [-h] [--message MESSAGE] [--file FILE]

optional arguments:
  -h, --help         이 도움말 메시지를 표시하고 종료합니다
  --message MESSAGE  메시지 문자열
  --file FILE        파일 이름
$ python3 main.py

인수 네임스페이스에서 파일과 메시지는 모두 None입니다.

# 실행 결과
Namespace(file=None, message=None)

기본 파일 이름과 메시지인 myfile.txtPython3이 사용됩니다. 이제 myfile.txt 파일이 작업 디렉토리에 있습니다.

$ ls
file1.txt main.py myfile.txt

그리고 대소문자가 바뀐 문자열 pYTHON3을 포함합니다.

$ cat myfile.txt
pYTHON3

--message--file 인수를 모두 사용하여 명령을 더 명확하게 만들 수도 있습니다.

$ python3 main.py --message Coding --file file2.txt
# 실행 결과
Namespace(file='file2.txt', message='Coding')

작업 디렉토리에 file2.txt가 표시됩니다.

$ ls
file1.txt file2.txt main.py myfile.txt

그리고 예상대로 문자열 ‘cODING’을 포함합니다.

$ cat file2.txt
cODING

결론

이 튜토리얼에서 배운 내용을 요약하면 다음과 같습니다.

  • C 프로그래밍 언어와 마찬가지로 파이썬에서는 sys.argv 인수 벡터를 사용하여 명령줄 인수에 접근할 수 있습니다. sys.argv[0]은 파이썬 스크립트의 이름이므로, 인수 구문 분석에 사용할 부분은 sys.argv[1:]입니다.
  • 가독성을 높이고 옵션을 추가하려면 getoptargparse 모듈을 사용할 수 있습니다.
  • getopt 모듈을 사용하면 인덱스 1부터 시작하여 명령줄 인수 목록을 구문 분석할 수 있으며, 짧은 옵션과 긴 옵션을 모두 지정할 수 있습니다.
  • 옵션이 인수를 사용하는 경우 짧은 옵션과 긴 옵션 뒤에 각각 콜론(:)과 등호(=)를 추가하여 지정할 수 있습니다.
  • 파이썬의 argparse 모듈을 사용하면 ArgumentParser 객체를 인스턴스화하고 add_argument() 메서드를 사용하여 필요한 위치 인수를 추가할 수 있습니다. 옵션으로 만들려면 인수 이름 앞에 --를 사용합니다.
  • 명령줄 인수 값을 가져오려면 ArgumentParser 객체에서 parse_args() 메서드를 호출합니다.

다음에는 파이썬에서 보안 해싱을 수행하는 방법을 알아보겠습니다.