Обработка внешних аргументов при запуске Python-скрипта

Раздел: Продвинутый Python -> Внешние данные

Работа с sys.argv в Python

Как получить список аргументов командной строки в Python?

Основной способ получения аргументов

Модуль sys предоставляет список argv, который содержит все переданные при запуске скрипта аргументы. Первый элемент (sys.argv[0]) это имя самого скрипта, остальные переданные аргументы.

import sys
print("Имя скрипта:", sys.argv[0])
print("Аргументы:", sys.argv[1:])

Python параметр скрипта (параметры командной строки скрипта python (sys.argv))

Запуск python script.py arg1 arg2 выведет:

Имя скрипта: script.py
Аргументы: ['arg1', 'arg2']

Пояснение: после импорта sys переменная sys.argv доступна. Срез sys.argv[1:] возвращает только переданные пользователем аргументы без имени скрипта. Это полезно для базовых сценариев, когда требуется просто получить значения позиционных параметров.

Типичные проблемы и их решения

Проблема: обращение к sys.argv[1] без проверки наличия аргументов вызывает IndexError. Решение: проверять длину len(sys.argv) перед доступом.

Проблема: все аргументы являются строками, даже числа. Решение: преобразовывать с помощью int() или float() с обработкой исключения ValueError.

Как использовать именованные аргументы (флаги) в командной строке?

Для сложных сценариев удобен модуль argparse. Он автоматически генерирует справку, обрабатывает типы и значения по умолчанию.

import argparse
parser = argparse.ArgumentParser(description="Пример использования argparse")
parser.add_argument("--input", help="Путь к входному файлу")
parser.add_argument("--output", default="out.txt", help="Путь к выходному файлу")
parser.add_argument("-v", "--verbose", action="store_true", help="Подробный вывод")
args = parser.parse_args()
print("Input:", args.input)
print("Output:", args.output)
print("Verbose:", args.verbose)

Пояснение: add_argument определяет аргументы. Для коротких флагов используется одна черта, для длинных две. action="store_true" делает флаг булевым. Запуск python app.py --input data.txt -v выведет соответствующие значения.

Проблема: неправильное имя аргумента, отсутствие обязательных. Решение: указывать required=True для обязательных аргументов.

Как без сторонних библиотек разобрать ключи вида -f или --file?

Можно написать простой парсер, перебирая sys.argv вручную.

import sys
args = sys.argv[1:]
options = {}
i = 0
while i < len(args):
    if args[i].startswith('--'):
        opt = args[i][2:]
        if i+1 < len(args) and not args[i+1].startswith('-'):
            options[opt] = args[i+1]
            i += 2
        else:
            options[opt] = True
            i += 1
    elif args[i].startswith('-'):
        opt = args[i][1:]
        if i+1 < len(args) and not args[i+1].startswith('-'):
            options[opt] = args[i+1]
            i += 2
        else:
            options[opt] = True
            i += 1
    else:
        options.setdefault('positional', []).append(args[i])
        i += 1
print(options)

Этот код обрабатывает как -f file, так и --verbose без значения. Позиционные аргументы собираются в список под ключом positional.

Проблема: не обрабатываются объединённые короткие ключи (-abc). Решение: усложнение парсера или переход на argparse.

Как использовать модуль getopt для разбора аргументов?

Модуль getopt старый, но иногда полезен. Он требует указать ожидаемые опции.

import sys, getopt
try:
    opts, args = getopt.getopt(sys.argv[1:], "hi:o:", ["help", "input=", "output="])
except getopt.GetoptError as err:
    print(err)
    sys.exit(2)
for opt, arg in opts:
    if opt in ("-h", "--help"):
        print("Справка")
    elif opt in ("-i", "--input"):
        print("Input:", arg)
    elif opt in ("-o", "--output"):
        print("Output:", arg)
print("Остальные аргументы:", args)

Пояснение: строка "hi:o:" определяет короткие опции: h без значения, i и o с обязательным значением (двоеточие). Длинные опции задаются списком, для опций со значением добавляется =. Запуск python app.py -i input.txt -o output.txt обработается корректно.

Проблема: getopt не поддерживает длинные опции без значений (флаги) легко. Решение: указывать в строке опций без двоеточия для флагов, а в списке без =.

Дополнительные примеры работы с аргументами командной строки

Пример 1: Калькулятор с поддержкой операций

Скрипт принимает числа и опционально операцию (sum, average, product). Используется argparse с nargs='+'.

Пример
import sys, argparse

parser = argparse.ArgumentParser(description='Простой калькулятор')
parser.add_argument('numbers', nargs='+', type=float, help='Числа для вычисления')
parser.add_argument('--operation', choices=['sum', 'average', 'product'], default='sum', help='Операция')
args = parser.parse_args()
if args.operation == 'sum':
    result = sum(args.numbers)
elif args.operation == 'average':
    result = sum(args.numbers) / len(args.numbers)
elif args.operation == 'product':
    result = 1
    for n in args.numbers:
        result *= n
print(f'Результат: {result}')

Результаты выполнения:

$ python calc.py 3 5 7 --operation sum
Результат: 15.0
$ python calc.py 3 5 7 --operation average
Результат: 5.0
$ python calc.py 3 5 7 --operation product
Результат: 105.0

Пояснение: nargs='+' означает один или более аргументов. type=float автоматически преобразует строки в числа. choices ограничивает допустимые значения опции.

Пример 2: Подкоманды (subparsers) как в Git

Использование вложенных парсеров для реализации команд add, list и т.д.

Пример
import argparse

parser = argparse.ArgumentParser(description='Утилита для работы с данными')
subparsers = parser.add_subparsers(dest='command', required=True)

parser_add = subparsers.add_parser('add', help='Добавить запись')
parser_add.add_argument('name', help='Имя')
parser_add.add_argument('--age', type=int, default=0, help='Возраст')

parser_list = subparsers.add_parser('list', help='Показать записи')
parser_list.add_argument('--sort', choices=['name', 'age'], default='name', help='Поле сортировки')

args = parser.parse_args()
if args.command == 'add':
    print(f'Добавлен {args.name}, возраст {args.age}')
elif args.command == 'list':
    print(f'Вывод списка, сортировка по {args.sort}')

Результаты:

$ python app.py add Alice --age 30
Добавлен Alice, возраст 30
$ python app.py list --sort age
Вывод списка, сортировка по age

Пояснение: subparsers создаёт отдельные группы аргументов для каждой команды. Аргумент dest='command' сохраняет имя команды.

Пример 3: Аргументы из переменной окружения или файла по умолчанию

Скрипт пытается получить значение из аргумента командной строки, затем из переменной окружения, затем использует значение по умолчанию.

Пример
import os, sys, argparse

parser = argparse.ArgumentParser()
parser.add_argument('--config', help='Путь к файлу конфигурации')
args, unknown = parser.parse_known_args()
config_file = args.config or os.environ.get('APP_CONFIG', 'config.ini')
print(f'Используется конфиг: {config_file}')

Результаты:

$ python app.py
Используется конфиг: config.ini
$ APP_CONFIG=/etc/app/config.ini python app.py
Используется конфиг: /etc/app/config.ini
$ python app.py --config /tmp/custom.ini
Используется конфиг: /tmp/custom.ini

Пояснение: parse_known_args() игнорирует неизвестные аргументы. Оператор or выбирает первое непустое значение.

Пример 4: Разбор строки с экранированием (shlex)

Когда аргументы приходят в виде строки (например, из другого скрипта), удобно использовать shlex.split().

Пример
import sys, shlex

# Имитация строки аргументов
cmdline = 'program "hello world" --name="John Doe"'
args = shlex.split(cmdline)[1:]  # убираем имя программы
print('Аргументы:', args)
# Теперь можно передать в любой парсер
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('message', nargs='?', default='default')
parser.add_argument('--name')
parsed = parser.parse_args(args)
print(parsed)

Результат:

Аргументы: ['hello world', '--name=John Doe']
Namespace(message='hello world', name='John Doe')

Пояснение: shlex.split() корректно обрабатывает кавычки и экранирование, возвращая список аргументов как если бы они были введены в терминале.

Пример 5: Переменное число позиционных аргументов (nargs='*')

Скрипт принимает любое количество файлов, по умолчанию использует stdin.

Пример
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('files', nargs='*', type=str, default=['-'], help='Файлы для обработки (по умолчанию stdin)')
parser.add_argument('--output', '-o', default='out.txt')
args = parser.parse_args()
print('Обрабатываемые файлы:', args.files)
print('Вывод в:', args.output)

Результаты:

$ python process.py
Обрабатываемые файлы: ['-']
Вывод в: out.txt
$ python process.py a.txt b.txt --output result.txt
Обрабатываемые файлы: ['a.txt', 'b.txt']
Вывод в: result.txt

Пояснение: nargs='*' принимает ноль или более аргументов. Значение default=['-'] используется, когда аргументы не переданы.

Параметры командной строки скрипта Python (sys.argv) - comments

En
Python параметр скрипта (python)