Аргументы запуска Python-скрипта при работе с файлами
Основные подходы к обработке аргументов командной строки в Python
При разработке скриптов на Python, особенно для задач файлового ввода-вывода, часто требуется передавать имена файлов, пути или параметры через командную строку. Ниже рассмотрены популярные способы получения этих аргументов, их назначение и типичные ошибки.
Каким образом можно гибко и надёжно разобрать аргументы командной строки с поддержкой флагов, типов и справки?
Основное решение: модуль argparse. Он предоставляет декларативный способ описания ожидаемых аргументов, автоматически генерирует справку и проверяет типы.
import argparse
parser = argparse.ArgumentParser(description='Чтение содержимого файла')
parser.add_argument('filename', help='Имя файла для чтения')
parser.add_argument('--encoding', default='utf-8', help='Кодировка файла')
args = parser.parse_args()
with open(args.filename, 'r', encoding=args.encoding) as f:
print(f.read())ввод программ на python (ввод данных в программе python)
Пояснение: add_argument определяет позиционный аргумент filename и опциональный флаг --encoding. После вызова parse_args() значения доступны как атрибуты объекта args. При запуске без аргументов или с флагом -h выводится справка.
Типичная ошибка: забыть указать обязательный аргумент. argparse сам выдаст сообщение об ошибке. Если требуется обработать ошибку по-своему, можно использовать parser.parse_known_args().
Как получить аргументы без установки дополнительных библиотек, только средствами стандартной библиотеки?
Используется sys.argv - список строк, переданных в командной строке. Первый элемент - имя скрипта.
import sys
if len(sys.argv) < 2:
print('Использование: python script.py filename')
sys.exit(1)
filename = sys.argv[1]
try:
with open(filename, 'r') as f:
print(f.read())
except FileNotFoundError:
print(f'Файл {filename} не найден')Python file io (ввод-вывод файлов в python)
Пояснение: вручную проверяется количество аргументов. Для более сложной логики (флаги, значения по умолчанию) это быстро становится громоздким.
Проблемы: отсутствие автоматической проверки типов, необходимость ручного разбора флагов, уязвимость к пробелам в путях (если не использовать кавычки в командной строке).
Каким способом можно поддерживать как короткие, так и длинные имена опций (например, -o file и --output file)?
Модуль getopt из стандартной библиотеки позволяет разбирать аргументы в стиле C. Он менее удобен, чем argparse, но иногда используется для совместимости.
import sys
import getopt
try:
opts, args = getopt.getopt(sys.argv[1:], 'ho:', ['help', 'output='])
except getopt.GetoptError as err:
print(err)
sys.exit(2)
output_file = None
for opt, arg in opts:
if opt in ('-h', '--help'):
print('Использование: ...')
sys.exit()
elif opt in ('-o', '--output'):
output_file = arg
if args:
input_file = args[0]
else:
print('Требуется входной файл')
sys.exit(1)
print(f'Вход: {input_file}, выход: {output_file}')
Python temp files (временные файлы в python)
Пояснение: getopt возвращает кортеж из опций и остальных аргументов. Строка 'ho:' означает: короткие опции h (без значения) и o: (с значением). Длинные аналоги указываются списком.
Типичная ошибка: забыть двоеточие после короткой опции, ожидающей значение. В результате значение не привяжется к опции. Также getopt не проверяет типы значений и не генерирует справку автоматически.
Как реализовать собственный разбор аргументов для очень простых сценариев, когда не хочется подключать целые модули?
Можно обработать sys.argv вручную, например, с помощью цикла и условных операторов. Этот подход подходит для скриптов с одним-двумя параметрами.
import sys
args = sys.argv[1:]
filename = None
mode = 'r'
i = 0
while i < len(args):
if args[i] == '-m' and i+1 < len(args):
mode = args[i+1]
i += 2
elif args[i].startswith('-'):
print(f'Неизвестная опция {args[i]}')
sys.exit(1)
else:
filename = args[i]
i += 1
if filename is None:
print('Не указан файл')
sys.exit(1)
print(f'Режим: {mode}, файл: {filename}')
Пояснение: вручную перебираются аргументы, распознаются флаги. Недостаток: код становится запутанным при росте числа опций.
Ошибки: легко допустить ошибку в индексах, не обработать крайние случаи, отсутствие поддержки отрицательных чисел как аргументов.
Расширенные примеры работы с аргументами командной строки
Пример 1: argparse с проверкой существования файла
import argparse
import os
def file_exists(path):
if not os.path.isfile(path):
raise argparse.ArgumentTypeError(f'Файл {path} не существует')
return path
parser = argparse.ArgumentParser(description='Подсчёт строк в файле')
parser.add_argument('file', type=file_exists, help='Путь к файлу')
parser.add_argument('--lines-start', type=int, default=1, help='Номер первой строки')
args = parser.parse_args()
with open(args.file, 'r') as f:
lines = f.readlines()
print(f'Всего строк: {len(lines)}, начиная с {args.lines_start}')
$ python script.py nonexistent.txt usage: script.py [-h] [--lines-start LINES_START] file script.py: error: argument file: Файл nonexistent.txt не существует $ python script.py existing.txt Всего строк: 42, начиная с 1
Пример 2: Обработка нескольких файлов с помощью sys.argv и glob
import sys
import glob
if len(sys.argv) < 2:
print('Укажите хотя бы один файл или шаблон (например, *.txt)')
sys.exit(1)
files = []
for pattern in sys.argv[1:]:
matched = glob.glob(pattern)
if not matched:
print(f'Предупреждение: нет файлов, соответствующих {pattern}')
files.extend(matched)
print('Обрабатываются файлы:')
for f in files:
with open(f, 'r') as fh:
print(f' {f}: {len(fh.readlines())} строк')
$ python script.py data/*.csv report.log Обрабатываются файлы: data/a.csv: 100 строк data/b.csv: 200 строк report.log: 50 строк
Пример 3: argparse с подкомандами (subparsers) для разных операций с файлами
import argparse
def read_file(args):
with open(args.file, 'r') as f:
print(f.read())
def write_file(args):
with open(args.file, 'w') as f:
f.write(args.content or '')
parser = argparse.ArgumentParser(description='Файловый менеджер')
subparsers = parser.add_subparsers(dest='command')
read_parser = subparsers.add_parser('read', help='Чтение файла')
read_parser.add_argument('file')
read_parser.set_defaults(func=read_file)
write_parser = subparsers.add_parser('write', help='Запись в файл')
write_parser.add_argument('file')
write_parser.add_argument('--content', help='Текст для записи')
write_parser.set_defaults(func=write_file)
args = parser.parse_args()
if hasattr(args, 'func'):
args.func(args)
else:
parser.print_help()
$ python file_manager.py read example.txt (содержимое файла) $ python file_manager.py write new.txt --content 'Hello' (создаёт файл с текстом)
Пример 4: Использование модуля fileinput для обработки файлов, переданных как аргументы
import fileinput
import sys
# Если файлы не указаны, читает stdin
for line in fileinput.input():
if fileinput.isfirstline():
print(f'--- {fileinput.filename()} ---')
sys.stdout.write(line)
$ python script.py file1.txt file2.txt --- file1.txt --- (содержимое file1.txt) --- file2.txt --- (содержимое file2.txt)
Пример 5: getopt с длинными опциями и проверкой целочисленных значений
import sys
import getopt
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], 'n:c:', ['number=', 'count='])
except getopt.GetoptError as err:
print(str(err))
sys.exit(1)
number = None
count = None
for opt, val in opts:
if opt in ('-n', '--number'):
try:
number = int(val)
except ValueError:
print('Опция --number требует целое число')
sys.exit(1)
elif opt in ('-c', '--count'):
count = int(val)
if args:
files = args
else:
print('Укажите хотя бы один файл')
sys.exit(1)
print(f'Число: {number}, количество: {count}, файлы: {files}')
if __name__ == '__main__':
main()
$ python script.py --number abc file.txt Опция --number требует целое число