Як розібрати аргументи командного рядка в Python

Бажаєте запускати ваші Python-скрипти з використанням аргументів командного рядка? Давайте розглянемо, як аналізувати ці аргументи за допомогою модулів `sys`, `getopt` та `argparse` у Python.

У Python для отримання даних від користувача зазвичай використовують функцію `input()`. Проте, в деяких ситуаціях необхідно передавати певні параметри безпосередньо при виконанні скрипту через командний рядок.

У цій статті ми навчимося, як запускати Python-скрипти з різними параметрами та аргументами, введеними через командний рядок. Згодом ми розглянемо, як використовувати вбудовані Python-модулі для обробки цих параметрів і аргументів.

Приступимо!

Розгляд `sys.argv` у Python

Якщо ви знайомі з програмуванням на C, то знаєте, що одним із найпростіших способів передачі аргументів до програми є використання командного рядка. Для цього основна функція може мати таку структуру:

#include<stdio.h>

int main(int argc, char **argv){
    //argc: лічильник аргументів
    //argv: вектор аргументів
    
    //дії з аргументами

    return 0;
}

Тут `argc` представляє кількість аргументів, а `argv` — вектор аргументів.

Виконання Python-скриптів з аргументами командного рядка

У Python скрипт можна запустити з командного рядка за допомогою команди `python3 filename.py`. Також можна додати будь-яку кількість аргументів командного рядка:

$ python3 filename.py arg1 arg2 ... argn

Модуль `sys` надає вбудовану підтримку для доступу та маніпулювання цими аргументами. `sys.argv` являє собою список всіх аргументів, переданих під час запуску Python-скрипту.

Наприклад, припустимо, що ми запускаємо файл `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) — це назва файлу Python. Наступні аргументи починаються з індексу 1.

Це базова програма, яка здатна приймати та обробляти аргументи командного рядка. Однак, вона має деякі недоліки:

  • Як користувачі програми дізнаються, які саме аргументи слід передавати?
  • Яке призначення цих аргументів?

Зрозумілість тут низька. Для вирішення цієї проблеми можна використовувати модулі `getopt` або `argparse`, про які ми поговоримо далі. ✅

Аналіз аргументів командного рядка за допомогою `getopt` у Python

Давайте навчимося обробляти аргументи командного рядка з використанням вбудованого модуля `getopt`.

Після імпортування `getopt` з модуля `getopt`, необхідно вказати аргументи для аналізу, а також короткі та довгі параметри для запуску скрипту. Нам потрібно обробити всі аргументи, починаючи з індексу 1 в `sys.argv`, тобто фрагмент для аналізу – `sys.argv[1:]`.

Припустимо, що програма очікує рядок повідомлення та ім’я файлу. Ми будемо використовувати `m` та `f` як короткі варіанти, а `message` та `file` як довгі.

Але як ми гарантуємо, що певний параметр потребує аргумент?

  • У коротких параметрах можна зробити так, щоб параметр вимагав аргументу, додавши двокрапку (`:`) після короткої назви параметра.
  • Аналогічно, у довгих параметрах можна додати знак `=` після довгого параметра. Ці параметри та їх відповідні аргументи можна буде зафіксувати.

Застосувавши це, отримаємо наступний код у `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`, який використовується для запуску модуля як основного при виконанні Python-скрипту.

Наприклад, команда `python3 -m unittest main.py` використовує `unittest` як основний модуль під час запуску `main.py`.

Як згадувалось раніше, всі інші позиційні аргументи будуть зібрані в змінній `args`. Ось приклад:

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

Список аргументів містить позиційний аргумент `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` з короткими параметрами та аргументами командного рядка:

$ python 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`, також входить до стандартної бібліотеки Python, забезпечує функціональність для обробки аргументів командного рядка та створення інтерфейсів командного рядка.

Для аналізу аргументів командного рядка імпортуємо клас `ArgumentParser` з модуля `argparse`. Створюємо екземпляр `arg_parser`, об’єкт класу `ArgumentParser`:

from argparse import ArgumentParser

arg_parser = ArgumentParser()

Далі нам потрібно додати два аргументи командного рядка:

  • `message`: рядок повідомлення;
  • `file`: назва файлу для роботи.

Викличемо метод `add_argument()` на `arg_parser` для додавання обох аргументів. У виклику методу `add_argument()` можна задати `help` (опис аргументу).

arg_parser.add_argument('message',help='рядок повідомлення')
arg_parser.add_argument('file',help='назва файлу')

Тепер у нас є екземпляр `arg_parser` і додано аргументи командного рядка. При запуску програми в командному рядку можна використовувати метод `parse_args()` на `arg_parser`, щоб отримати значення аргументів.

Тут простір імен аргументів зберігається в змінній `args`. Отже, можна використовувати `args.argument_name`, щоб отримати значення конкретного аргументу.

Отримавши значення аргументів, запишемо рядок повідомлення зі зміною регістру літер (за допомогою методу `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  показує довідку та виходить

Тут немає додаткових аргументів, і `message` та `file` – обов’язкові позиційні аргументи. Також можна використовувати коротку опцію `-h`:

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

positional arguments:
  message     рядок повідомлення
  file        назва файлу

optional arguments:
  -h, --help  показує довідку та виходить

Як бачимо, обидва аргументи за замовчуванням є позиційними. Якщо не передати один або декілька з цих аргументів, виникне помилка.

Тут ми передали позиційний аргумент (“Hello”) для рядка повідомлення, але не вказали значення для аргументу `file`.

В результаті виникає помилка про відсутність аргументу `file`.

$ 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` таким чином, щоб аргументи `message` та `file` стали необов’язковими.

# 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())

Перевіривши використання, побачимо, що і `message`, і `file` тепер необов’язкові. Це означає, що можна запускати `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

У просторі імен аргументів `file` та `message` мають значення `None`.

# Вивід
Namespace(file=None, message=None)

Як бачимо, використовуються назва файлу та повідомлення за замовчуванням: `myfile.txt` та `Python3`. Файл `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, у Python можна отримати доступ до аргументів командного рядка, перебираючи вектор аргументів `sys.argv`. `sys.argv[0]` є назвою Python-скрипту. Отже, нас цікавить аналіз аргументів `sys.argv[1:]`.
  • Для підвищення читабельності та можливості додавання параметрів можна використовувати модулі `getopt` та `argparse`.
  • Модуль `getopt` використовується для аналізу списку аргументів командного рядка, починаючи з індексу 1 і до кінця списку. Можна вказувати як короткі, так і довгі варіанти параметрів.
  • Коли параметр приймає аргумент, слід додати двокрапку (`:`) та знак `=` після короткого та довгого параметрів відповідно.
  • Модуль `argparse` дозволяє створити екземпляр об’єкта `ArgumentParser` та використовувати метод `add_argument()` для додавання потрібного позиційного аргументу. Щоб зробити аргумент необов’язковим, потрібно додати перед його назвою `–`.
  • Для отримання значень аргументів командного рядка потрібно викликати метод `parse_args()` об’єкта `ArgumentParser`.

Далі ви можете ознайомитися з безпечним хешуванням у Python.