sys.argv Python

Введение

Это статья про агрументы, которые передаются в скрипт извне. Например, из терминала.

Передача аргументов в скрипт это распространённая практика для CI/CD.

Про обычные аргументы, то есть такие, которые передаются в функцию внутри модуля, читайте статью *args, **kwargs

Чтобы передать аргументы в Python скрипт из командной строки нужно воспользоваться библиотекой sys

import sys

Далее каждый аргумент нужно принимать с помощью sys.argv

a = sys.argv[0] b = sys.argv[1] c = sys.argv[2] …

sys.argv[0]

Нулевой элемент это название самого скрипта.

# zero.py import sys print(f"First argument argv[0] is {sys.argv[0]}")

python zero.py

Можно передать ещё какие-то аргументы, ошибки не будет.

python zero.py argument

First argument argv[0] is .\zero.py

В Linux

python3 zero.py

First argument argv[0] is zero.py

sys.argv[1]

Первый элемент это первый переданный аргумент.

# one.py import sys print(f"Second argument argv[1] is {sys.argv[1]}")

Такой скрипт нельзя вызвать без аргументов.

python .\one.py

Traceback (most recent call last): File "C:\python\one.py", line 4, in <module> print(f"Second argument argv[1] is {sys.argv[1]}") ~~~~~~~~^^^ IndexError: list index out of range

python .\one.py Test

Second argument argv[1] is Test

О том как сделать скрипт устойчивым к непередаче аргумента читайте в следующей главе.

Необязательный аргумент

Если нужно сохранить возможность не передавать никаких аргументов - можно добавить проверку длинны sys.argv

# one.py import sys if len(sys.argv) > 1: a = sys.argv[1] else: a = None print(f"Second argument argv[1] is {a}")

python .\one.py

Second argument argv[1] is None

python .\one.py Test

Second argument argv[1] is Test

Можно задать строгое условие на количество аргментов:

import sys if len(sys.argv) == 2: a = sys.argv[1] else: a = "default" print("a =", a) print(type(a))

python sysargs.py 2

a = 2 <class 'str'>

python sysargs.py

a = default <class 'str'>

Неизвестное заранее количество аргументов

Что делать если число аргументов заранее неизвестно.

Используем следующее свойство списков в Python

lst = ["a", "b", "c"] print(lst[1:])

['b', 'c']

import sys print(f"Script received positional args: {sys.argv[1:]}")

python positional.py arg1 arg2 'arg as phrase'

Script received positional args: ['arg1', 'arg2', 'arg as phrase']

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

Пример

Скрипт add.py, который складывает две строки.

Без проверки на существование аргумента.

# one.py import sys first = sys.argv[1] second = sys.argv[2] print(f"{first} + {second} = {first + second}")

python add.py topbicycle .ru

topbicycle + .ru = topbicycle.ru

В такой скрипт можно передать ровно два аргумента.

Рассмотрим похожий скрипт, успешно принимающий разное количество аргументов.

import sys st = "" for el in sys.argv[1:]: st += el print(st)

В этот скрипт можно передавать разное количество аргументов

python unknown_len.py

python unknown_len.py 1 2

12

python unknown_len.py 1 2 3 a b cde 4 5 None

123abcde45None

Чтобы проделать похожие действия с целыми числами - используем функцию int() так как даже если передать числа, они будут получены как строки.

import sys first = int(sys.argv[1]) second = int(sys.argv[2]) print(f"{first} + {second} = {first + second}")

python add.py 3 4

3 + 4 = 7

Теперь то же самое но для заранее неизвестного числа элементов. Некоторые элементы могут быть неприводимы к типу int.

Для разнообразия используем range(1, len(sys.argv)) вместо sys.argv[1:]

import sys s = 0 x = 0 for i in range(1, len(sys.argv)): try: x = int(sys.argv[i]) except ValueError: x = 0 finally: s += x print(s)

python unknown_len.py 1 2 3 4 5

15

Можно передать в скрипт и нечисловые аргументы

python unknown_len.py 1 2 3 a b cde 4 5 None

15

Можно ничего не передавать, ошибки не будет. Сумма будет равна нулю.

python unknown_len.py

0

Пример 2

Рассмотрим скрипт words.py из курса от Pluralsight

import sys from urllib.request import urlopen def fetch_words(url): # story = urlopen("https://testsetup.site/testdata/words_en.txt") story = urlopen(url) story_words = [] for line in story: line_words = line.decode("utf8").split() for word in line_words: story_words.append(word) story.close() return story_words def print_words(story_words): for word in story_words: print(word) def main(): url = sys.argv[1] words = fetch_words(url) print_words(words) if __name__ == "__main__": main()

Этот скрипт не будет работать если не передать нужный url через терминал

python words.py "https://testsetup.site/testdata/words_en.txt"

Желательно также предусмотреть вызов функции main() после импорта.

В текущем виде будут проблемы

python

Python 3.9.5 (default, Jun 15 2021, 15:30:04) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information.

>>> from words import *
>>> main()

Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/andrei/python/words.py", line 23, in main url = sys.argv[1] IndexError: list index out of range

>>> main("https://testsetup.site/testdata/words_en.txt")

Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: main() takes 0 positional arguments but 1 was given

Решается проблема добавлением sys.argv[1] в вызов main

def main(url): words = fetch_words(url) print_words(words) if __name__ == "__main__": main(sys.argv[1])

>>> main("https://testsetup.site/testdata/words_en.txt")

В других языках

В C аналогичную функцию выполняет int argc, char* argv[]

В Bash это встроено по умолчанию .

Article author: Andrei Olegovich