![]() |
Обработка аргументов командной строки в Python |
![]() |
Задача разбора аргументов командной строки
При запуске любой программы существует возможность передавать ей параметры, называемые аргументами командной строки (далее - АКС). Несмотря на название, АКС можно передавать и графическим приложениям. Формат и содержание АКС могут быть произвольными и определяются кодом самой программы. Каждый язык программирования предлагает средства для получения и обработки АКС (чаще всего - в виде массива или списка строк).
Под задачей разбора АКС понимается процесс извлечения из АКС каких-либо значений и сохранение их в отдельных переменных программы. Это делается для того, чтобы в дальнейшем разработчику было удобнее получать доступ к значениям конкретных АКС.
Если программа имеет большое количество возможных АКС, задача их разбора усложняется. Написание обрабатывающего кода для каждого аргумента оказывается монотонной работой, в которой легко допустить ошибку. Для автоматизации разбора АКС многие ЯП предоставляют соответствующие библиотеки. Идея, заложенная в них, заключается в следующем:
- Разработчик с помощью функций или классов библиотеки описывает допустимые для своей программы АКС в декларативном стиле.
- Библиотека производит объект синтаксического анализатора (англ. parser), который будет производить синтаксический разбор АКС в соответствии с декларативным описанием.
- На вход объекту подаются фактические АКС.
- В результате своей работы парсер “раскладывает” значения АКС по соответствующим переменным.
К библиотекам разбора АКС относятся: getopt для C, boost::program_options для C++, CommandLineParser для C#, optparse-applicative для Haskell и argparse для Python, с которой мы познакомимся в этой работе.
Структура аргументов командной строки
В результате появления библиотек для разбора АКС, и несмотря на отсутствие стандартизации в этой области, сложились негласные правила форматирования АКС. Согласно этим правилам, АКС подразделяются на следующие группы:
- Флаги (англ. flags).
- Опции (англ. options).
- Позиционные аргументы (англ. positional arguments).
- Команды (англ. commands или subcommands).
Флаги и опции предваряются одним или несколькими специальными символами, которые
и отличают их от позиционных аргументов и команд. Наиболее распространённым
символов является '-', а в ОС Windows традиционно используется '/'. Спецсимвол
пишется один раз перед однобуквенными флагами и опциями и дважды перед
многобуквенными. Опции отличаются от флагов тем, что они требуют значение в
качестве параметра, в то время как флаги параметров не имеют. Некоторые флаги и
опции могут одновременно иметь краткий однобуквенный и длинный многобуквенный
варианты. Рассмотрим применение флагов и опций на примере консольных приложений
Unix-подобных ОС:
# выводит список файлов в текущей директории в виде списка
ls -l
# выводит количество слов и строк в файле input.txt
wc -w -l input.txt
# находит все вхождения слова "what" во всех файлах внутри директории "where"
grep -r --color what where
# выводит первые 5 строк из файла input.txt
head -n 5 input.txt
АКС -l, -w, -r и --color в примере выше являются опциями, т.к. не
принимают никаких значений в качестве параметров; АКС -n является опцией, т.к.
он принимает количество строк, которые необходимо вывести; АКС input.txt,
what и where - позиционными аргументами, т.к. не начинаются со спецсимвола.
Некоторые библиотеки позволяют объединять несколько однобуквенных флагов или опций,
идущих подряд, в один АКС без потери смысла. Например, команду wc из примера
выше можно записать так:
wc -wl input.txt
Параметры однобуквенных опций можно так же объединять с самой опцией:
head -n5 input.txt
Многобуквенные опции отделяются от параметра пробелом или символом =:
# распаковывает архив archive.tar
tar -xv --file=archive.tar
Позиционные аргументы и команды не имеют в начале никаких специальных символов. Позиционными аргументами, чаще всего, являются имена файлов или адреса каких либо ресурсов:
# удаляет file1.txt и file2.txt
rm file1.txt file2.txt
# посылает эхо-запрос на сайт onlyfans.com
ping onlyfans.com
Позиционные аргументы называются так, потому что могут менять смысл в зависимости от своей позиции:
# копирует file1.txt в file2.txt
cp file1.txt file2.txt
# наоборот, копирует file2.txt в file1.txt
cp file2.txt file1.txt
Командами называются АКС, которые разрешают или запрещают использование других АКС, или же меняют их смысл:
# В ОС FreeBSD выводит аннотацию для установленного пакета
# "info" - команда, которая выводит информацию о заданном пакете
# "-A" - флаг, который при команде info выводит аннотацию
pkg info -A firefox
# Устанавливает пакет и помечает его как установленный автоматически
# "install" - команда для установки пакетов
# "-A" - флаг, который при команде install помечает его автоматически установленным
pkg install -A firefox
Библиотека argparse
В этом разделе изложим краткое руководство по использованию модуля argparse.
Подробная информация о нём находится в официальной документации Python.
Перед началом использования библиотеки необходимо импортировать соответствующий модуль в свою программу:
import argparseПарсер создаётся вызовом конструктора ArgumentParser(), а его запуск производится
с помощью метода parse_args():
parser = argparse.ArgumentParser()
parser.parse_args()Несмотря на то, что мы ещё не объявили ни одного АКС, argparse автоматически
создаёт флаг -h,--help, который выводит справку по использованию программы.
Все остальные АКС должны объявляться разработчиком перед вызовом parse_args().
Перечислим некоторые именованные аргументы конструктора ArgumentParser():
prog. Имя программы, которое будет отображаться в справке. Значение по умолчанию -sys.argv[0].description. Текстовое описание, которое отображается в справке перед информацией о доступных АКС.epilog. Текстовое описание, которое отображается в справке после информации о АКС.prefix_chars. Множество символов, обозначающих начало флага или опции. Значение по умолчанию -'-'.allow_abbrev. Определяет, допустимо ли сокращать многобуквенные флаги и опции. Если включено, то вместо--longoptionможно писать, например,--longop. Значение по умолчанию -True.exit_on_error. Определяет, должен ли вызовparse_args()приводить к завершению программы, если один или несколько АКС не могут быть корректно разобраны.
Перепишем пример выше с использованием этих аргументов:
parser = argparse.ArgumentParser(prog="myprog", description="My awesome program",
epilog="All rights reserved", prefix_chars="-+",
allow_abbrev=False, exit_on_error=False)
parser.parse_args()Добавление АКС осуществляется с помощью метода add_argument():
parser.add_argument("filename")В примере выше объявляется позиционный аргумент с именем filename. Запустив
программу с флагом -h можно увидеть, что этот аргумент появился в справке.
Рассмотрим подробно параметры метода:
- Первыми и неименованными параметрами должны быть имена создаваемого флага,
опции или позиционного аргумента. Библиотека отличает позиционные аргументы
от флагов и опций по наличию предваряющего спецсимвола. Например,
.add_argument("-a", "--aaaa", "--bbbb")создаёт синонимичные флаги-a,--aaaaи--bbbb. action. Задаёт действие, которое должен совершить парсер, когда он обнаруживает данный АКС в строке. Значениями данного параметра могут быть следующие строки:'store'. Сохраняет значение опции в соответствующую ей переменную. Является значением по умолчанию.'store_const'. Сохраняет константу в переменную, соответствующую опции. Сама константа передаётся через другой параметр -const.'store_false'и'store_true'. Специальные вариантыstore_const, которые сохраняютFalseиTrue, соответственно.'append'. Работает подобноstore, но если опция указана несколько раз, то не перезаписывает хранимое значение, а составляет список из всех переданных значений.'append_const'. Подобноappend, дописывает в список указанную константу.'count'. Подсчитывает сколько раз флаг перечислен в командной строке.'extend'. Работает подобноappend, но позволяет дописывать в список сразу несколько значений при переданном параметреnargs.
nargs. По умолчаниюargparseпроизводит одно действие для каждого отдельного обрабатываемого АКС. С помощью этого параметра можно изменить это поведение. Значениямиnargsмогут быть:- целое число. Группирует заданное количество значений опции или позиционного аргумента.
'?'. Значение опции или позиционного аргумента берётся из командной строки, если оно там указано. Иначе - значение берётся из параметраdefault, если опция указана без значения и из параметраconstв остальных случаях.'*'. Все значения собираются в список.'+'. Все значения собираются в список, причём опция или позиционный аргумент должны быть указаны хотя бы один раз.
const. Применяется для задания значения, которое будет ассоциированно с АКС, не имеющими соответствующих им значений в командной строке. Два наиболее распространённых случая, в которых применяетсяconst:- Когда
add_argument()вызван сaction='store_const'илиaction='append_const'этот параметр определяет какое именно значение будет установлено или добавлено в список значений АКС. - Когда
add_argument()определяет опцию вида-f,--fooи вызван сnargs='?'. В этом случае значениеconstиспользуется, если опция в командной строке была указана без значения.
- Когда
default. Для опций и позиционных аргументов указывает значение, которое должно быть использовано, если эта опция или аргумент не указаны в командной строке.type. Задаёт функцию, которая будет вызвана над значением опции или позиционного аргумента. Чаще всего используется для конвертирования строковых значений в нужные пользователю. В качестве значения этого параметра может быть использована любая функция, принимающая один параметр-строку.choices. Используется для создания опции или позиционного аргумента, которые должны принимать строго определённый набор значений.required. Булевый параметр, определяющий, является ли АКС обязательным. Значение по умолчанию:False.metavar. Устанавливает текст, который будет выводиться в справке вместо параметров опций и позиционных аргументов.dest. Задаёт имя поля, в которое будет сохранено значение АКС. Если не указано, то вычисляется из имени флага, опции или позиционного аргумента.
Рассмотрим подробный пример, использующий описанные выше параметры:
# опция -f с синонимом --foo
parser.add_argument("-f", "--foo")
# флаг -g, сохраняющий значение 42, если он был передан
parser.add_argument("-g", action="store_const", const=42)
# флаги --yes и --no, сохраняющие, если переданы, True и False, соответственно.
parser.add_argument("--yes", action="store_true")
parser.add_argument("--no", action="store_false")
# опция -h, который можно указывать несколько раз, значения которого будут собраны в список
parser.add_argument("-h", action="append")
# флаги --with-pasta и --with-pizza, которые, если указаны, собирают в список
# под именем order строковые константы "pasta" и "pizza", соответственно
parser.add_argument("--with-pasta", action="append_const", dest="order", const="pasta")
parser.add_argument("--with-pizza", action="append_const", dest="order", const="pizza")
# флаг -v, сохраняющий количество вхождений в командную строку
# например, "-v -v -v" будет сохранять 3
parser.add_argument("-v", action="count", default=0)
# опция --items, которую можно указывать несколько раз, и которая может иметь
# одно или более значений
# например, --items ball --items pen boots сохранит ["ball", "pen", "boots"]
parser.add_argument("--items", action="extend", nargs="+")
# опция --between, принимающая строго два параметра
parser.add_argument("--between", nargs=2)
# позиционный параметр infile, который если не указан, использует значение по
# умолчанию "input.txt"
parser.add_argument("infile", nargs="?", default="input.txt")
# позиционный параметр direction, который может принимать
# только значения "up", "down", "left" и "right"
parser.add_argument("direction", choices=["up", "down", "left", "right"])В результате успешного завершения метода parse_args() возвращается объект,
содержащий значения разобранных АКС в качестве своих полей. Имена полей
определяются параметром dest при вызове add_argument():
parser = argparse.ArgumentParser()
parser.add_argument("filename")
args = parser.parse_args()
print(args.filename)Формат CSV
Для выполнения данной работы необходимо также познакомиться с форматом CSV.
CSV (англ. Comma Separated Values, значения разделённые запятыми) - текстовый формат представления простых табличных данных. Для создания CSV-файла достаточно обычного текстового редактора, однако табличные процессоры вроде LibreOffice Calc и Microsoft Excel так же имеют возможность экспортировать данные в CSV. Пример содержимого простейшего CSV-файла приведён ниже:
ФИО,Стаж,Зарплата
Дегтярёв Данил Игоревич,2,7000
Попов Глеб Александрович,7,300000/сек
Несмотря на название, CSV-файлы иногда разделяются не символами ,, а какими-либо
другими. Microsoft Excel разделяет ячейки с помощью ; или символа табуляции,
а так же имеет некоторые другие особенности форматирования.
Для чтения, обработки и записи CSV-файлов в языке Python имеется библиотека csv.
Ознакомимся с её программным интерфейсом.
Функция reader() возвращает объект-читатель, который позволяет считывать
содержимое CSV-файла построчно. Идея этого объекта во многом напоминает идею,
стоящую за потоками ввода/вывода (StreamReader и FileStream в C#). В качестве
первого её параметра передаётся любой итерируемый объект, из которого будет
черпаться информация. Это означает, что объект-читатель CSV может читать данные
не только из файлов, но и, например, из переменных-списков. Именованные аргументы
функции reader() включают:
delimiter. Задаёт символ-разделитель в CSV-файле. По умолчанию -','.quotechar. Задаёт символ, которым обрамляются ячейки, содержащие спецсимволы. По умолчанию -'"'.escapechar. Задаёт символ, который используется для экранирования спецсимволов.doublequote. Определяет как экранировать символquotechar, если он встречается в самой ячейке. При значенииFalseк этому символу приписывается символescapechar, приTrue- символ дублируется. По умолчанию -True.skipinitialspace. Определяет, игнорировать ли пробелы, следующие сразу за символом-разделителем. По умолчанию -False.
Функция writer() принимает тот же набор аргументов, что и reader(), и
возвращает объект-писатель. Этот объект аналогичен StreamWriter в C# и позволяет
построчно записывать данные в формате CSV.
Объект-читатель может производить чтение построчно путем организации цикла for
по этому объекту. Чтобы прочитать файл целиком, объект-читатель нужно сконвертировать
в список.
Объект-писатель имеет метод writerow(), принимающий список ячеек и записывающий
очередную строку в CSV-файл, а так же метод writerows(), принимающий список
списков ячеек.
Важно иметь ввиду, что при вызове reader() или writer() над файлами,
необходимо обязательно указывать параметр newline='' при их открытии.
Приведём пример, демонстрирующий применение описанных функций:
import csv
infile = open("input.csv", newline='')
reader = csv.reader(infile, delimiter='\t', quotechar='|', skipinitialspace=True)
for line in reader:
for cell in line:
print("Ячейка содержит " + cell)
outfile = open("output.csv", "w", newline='')
writer = csv.writer(outfile)
l1 = ["a", "b", "c"]
l2 = ["x", "y", "z"]
writer.writerow(l1)
writer.writerow(l2)Более подробную информацию о модуле csv можно найти в
официальной документации.
Контрольные вопросы
- Виды аргументов командной строки и их отличия.
- Что такое парсер АКС и какую задачу он решает?
- Что означают символы
{}и[]в справке по использованию программы? - Что такое экранирование символов?
Задание на лабораторную работу
- Написать программу на Python, обрабатывающую аргументы командной строки в соответствии с вариантом. Программа должна прочитывать матрицу из CSV-файла и производить над ней операции, в зависимости от указанных аргументов командной строки. Изменённая матрица должна выводиться в выходной CSV-файл, путь до которого так же указывается в аргументах командной строки. В программе должна присутствовать обработка ошибочного ввода.
- Запустить написанную программу и вывести справку по её использованию, а затем убедиться в ее правильном функционировании при введённых входных данных.
Варианты заданий
Вариант 1
- Аргумент
-i,--inputзадаёт путь до входного CSV-файла. Обязательный аргумент. - Аргумент
-o,--outputзадаёт путь до выходного CSV-файла. Если не указан - имя генерируется на основе входного файла. - Аргумент
-v. Если он указан, то программа выводит на экран промежуточные значения во время своей работы. - Обязательный параметр, принимающий лишь значения “rows” и “cols”. В зависимости от значения этого параметра, программа меняет строки или столбцы матрицы между собой местами.
Вариант 2
- Обязательный параметр, задающий путь до входного CSV-файла.
- Обязательный параметр, задающий путь до выходного CSV-файла.
- Аргумент
-n, задающий номер строки матрицы, в которую программа должна поместить суммы элементов всех остальных строк, включая эту. Значение по умолчанию -0. - Аргумент
-q,--quiet, необязательный. Если он указан, то программа подавляет любые сообщения об ошибках.
Вариант 3
- Обязательный параметр, задающий путь до входного CSV-файла.
- Выходной файл не указывается, программа всегда перезаписывает входной CSV-файл.
- Аргумент
-b,--backup, определяющий, нужно ли создавать копию входного CSV-файла перед его перезаписью. - Аргумент
-r,--row, задающий номер столбца, половину элементов которого программа должна занулить.
Вариант 4
- Аргумент
-i,--inputзадаёт путь до входного CSV-файла. Обязательный аргумент. - Аргумент
-o,--outputзадаёт путь до выходного CSV-файла. Если не указан - имя генерируется на основе входного файла. - Аргумент
-s, задающий размер квадратной матрицы, до которой программа должна урезать исходную. Если исходная матрица имеет меньший размер, чем значение параметра-s, то программа должна выводить ошибку. - Аргумент
-n, необязательный. Если он указан, то программа не выполняет обрезание матрицы, а лишь проверяет, возможно ли это для заданного-s.
Вариант 5
- Обязательный параметр, задающий путь до входного CSV-файла.
- Обязательный параметр, задающий путь до выходного CSV-файла.
- Аргумент
-a,--action, принимающий только значения “width” или “height”. В зависимости от значения, добавляет в матрицу строку, состоящую из одинаковых чисел, равных ширине или высоте исходной матрицы. - Аргумент
-p,--prepend, необязательный. Если указан, то строка добавляется не в конец матрицы, а в её начало.
Вариант 6
- Обязательный параметр, задающий путь до входного CSV-файла.
- Выходной файл не указывается, программа всегда перезаписывает входной CSV-файл.
- Аргумент
-r,--row, задающий номер строки, над которой оперирует программа. - Аргумент
-e, принимающий числовые значения, которыми программа должна заменять элементы указанной строки. Может повторяться несколько раз, т.е. аргументы-e 1 -e 2 -e 3должны приводить к тому, что в строке первый, второй и третий элементы меняются на 1, 2 и 3, соответственно.
Вариант 7
- Аргумент
-i,--inputзадаёт путь до входного CSV-файла. Обязательный аргумент. - Аргумент
-o,--outputзадаёт путь до выходного CSV-файла. Если не указан - имя генерируется на основе входного файла. - Аргумент
-q,--quiet, необязательный. Если он указан, то программа подавляет любые сообщения об ошибках. - Аргумент
-xзадаёт номер строки, над которым будет работать программа. Значение по умолчанию -1. - Аргумент
-yзадаёт номер столбца, над которым будет работать программа. Значение по умолчанию -1.
Программа зануляет элементы матрицы вокруг элемента, указанного через -x и -y.
Если квадрат, элементы которого зануляются, выходит за пределы матрицы (напр.
-x 0 -y 0), то программа выдаёт ошибку.
Вариант 8
- Обязательный параметр, задающий путь до входного CSV-файла.
- Обязательный параметр, задающий путь до выходного CSV-файла.
- Аргумент
-v. Если он указан, то программа выводит на экран промежуточные значения во время своей работы. - Аргумент
-s,--start. Принимает числовое значение, на которое программа заменяет элементы главной диагонали матрицы, с каждым шагом увеличивая это значения на 1. Обязательный аргумент. - Аргумент
--stop, необязательный. Принимает числовое значение, определяющее количество изменений, вносимых программой в матрицу. При значении0должна заменяться вся диагональ. Значение по умолчанию -0.
Вариант 9
- Обязательный параметр, задающий путь до входного CSV-файла.
- Выходной файл не указывается, программа всегда перезаписывает входной CSV-файл.
- Аргумент
-r1, задающий номер строки, с которой программа начинает работу. - Аргумент
-r2, задающий номер строки, с которой программа заканчивает работу. - Аргумент
-b,--backup, определяющий, нужно ли создавать копию входного CSV-файла перед его перезаписью.
Программа суммирует строки от r1 до r2 поэлементно, а получившуюся строку
подставляет в матрицу вместо строк, использовавшихся для суммирования. Если
-r1 не указан, то программа использует строки от первой до r2. Если -r2
не указан, то программа использует строки от r1 до последней.
Вариант 10
- Аргумент
-i,--inputзадаёт путь до входного CSV-файла. Обязательный аргумент. - Аргумент
-o,--outputзадаёт путь до выходного CSV-файла. Если не указан - имя генерируется на основе входного файла. - Обязательный параметр, принимающий лишь значения “r2c” и “c2r”. В зависимости от значения, производит транспонирование матрицы, переставляя строки на место столбцов, или наоборот.
- Аргумент
-q,--quiet, необязательный. Если он указан, то программа подавляет любые сообщения об ошибках.
Вариант 11
- Обязательный параметр, задающий путь до входного CSV-файла.
- Обязательный параметр, задающий путь до выходного CSV-файла.
- Аргумент
-v. Если он указан, то программа выводит на экран промежуточные значения во время своей работы. - Аргумент
-r,--row. Задаёт номер элемента, на основании которого программа должна отсортировать строки матрицы. - Аргумент
-o,--order, принимающий только значения “ascending” и “descending”. Определяет, будет ли сортировка производиться по возрастанию или по убыванию. Обязательный аргумент.
Вариант 12
- Обязательный параметр, задающий путь до входного CSV-файла.
- Выходной файл не указывается, программа всегда перезаписывает входной CSV-файл.
- Аргумент
-x1задаёт номер строки, над которым будет работать программа. Значение по умолчанию -1. - Аргумент
-y1задаёт номер столбца, над которым будет работать программа. Значение по умолчанию -1. - Аргумент
-x2задаёт номер строки, над которым будет работать программа. Значение по умолчанию -1. - Аргумент
-y2задаёт номер столбца, над которым будет работать программа. Значение по умолчанию -1.
Программа вырезает из матрицы прямоугольник элементов, левый верхний угол которого
имеет индекс (x1,y1), а правый нижний - (x2,y2). В выходной файл записывается
вырезанная подматрица. Программа не должна допускать вырезания единственного
элемента, если только не указан необязательный параметр -o.
Вариант 13
- Аргумент
-i,--inputзадаёт путь до входного CSV-файла. Обязательный аргумент. - Аргумент
-o,--outputзадаёт путь до выходного CSV-файла. Если не указан - имя генерируется на основе входного файла. - Аргумент
-r,--rowзадаёт номер строки матрицы, над которой будет работать программа. Значение по умолчанию -0. - Аргумент
-d,--directionпринимает лишь значения “right” и “left” и задаёт направление сдвига аргумента-s. - Аргумент
-s,--shiftзадаёт на сколько нужно сдвинуть элементы внутри строки. Последний элемент при сдвиге вправо становится первым, и наоборот.
Вариант 14
- Обязательный параметр, задающий путь до входного CSV-файла.
- Обязательный параметр, задающий путь до выходного CSV-файла.
- Аргумент
-xзадаёт номер строки, над которым будет работать программа. Значение по умолчанию -1. - Аргумент
-yзадаёт номер столбца, над которым будет работать программа. Значение по умолчанию -1. - Аргумент
-n, который может быть указан несколько раз, задаёт числа, количество которых программа должна подсчитать в квадрате вокруг элемента с индексами-xи-y. Этот элемент заменяется на количество найденных элементов. Если квадрат выходит за пределы матрицы (напр.-x 0 -y 0), то программа выдаёт ошибку.
Вариант 15
- Обязательный параметр, задающий путь до входного CSV-файла.
- Выходной файл не указывается, программа всегда перезаписывает входной CSV-файл.
- Аргумент
-b,--backup, определяющий, нужно ли создавать копию входного CSV-файла перед его перезаписью. - Аргумент
-f, который может быть указан несколько раз, задаёт последовательность элементов, которую программа должна искать в каждой строке матрицы. При обнаружении последовательности строка удаляется. - Аргумент
-d, необязательный. Если указан, программа лишь печатает номера строк, которые будут удалены, но не удаляет их.



