Команда Python для запуска Linux

Обновлено: 24.11.2024

Знание того, как выполнить команду оболочки в Python, поможет вам создавать программы для автоматизации задач в вашей системе.

Существует несколько способов выполнить команду оболочки в Python. Самые простые используют функции os.system и os.popen. Рекомендуемым модулем для запуска команд оболочки является модуль подпроцесса Python из-за его гибкости в предоставлении вам доступа к стандартному выводу, стандартным ошибкам и командному конвейеру.

Мы начнем это руководство с модуля ОС, а затем перейдем к модулю подпроцесса.

Это даст вам полное представление о том, как обрабатывать команды оболочки в Python.

Начнем программировать!

Использование системы ОС для запуска команды в Python

Я создал простой скрипт Python с именем shell_command.py.

Он использует системную функцию модуля os для запуска команды даты Linux:

Это результат работы функции os.system():

Давайте посмотрим, что произойдет, если мы запустим тот же код в оболочке Python:

Мы по-прежнему видим вывод команды date, но также видим 0 в последней строке. Это код выхода команды Linux.

Успешная команда в Linux возвращает код выхода 0, а в случае сбоя возвращается ненулевой код выхода.

Давайте подтвердим это, допустив орфографическую ошибку в команде даты:

Обратите внимание, что статус выхода отличается от статуса, возвращаемого оболочкой Bash:

Я написал еще одну статью, в которой объясняются коды выхода Bash, если вы хотели бы узнать об этом больше.

Позже в этой статье мы сравним os.system и другой модуль Python под названием subprocess.

Использование OS Popen для выполнения команд

Используя os.system(), мы не можем сохранить вывод команды Linux в переменную. И это одна из самых полезных вещей при написании сценария.

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

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

Функция popen возвращает объект открытого файла, и для чтения его значения можно использовать метод read:

Посмотрите, что произойдет, если мы снова воспользуемся методом чтения для того же объекта:

Результатом является пустая строка, потому что мы можем прочитать из файлового объекта только один раз.

Мы можем использовать os.popen и функцию чтения в одной строке:

При выполнении команд оболочки в Python важно понимать, выполняется команда успешно или нет.

Для этого мы можем использовать метод close файлового объекта, возвращаемый os.popen. Метод close возвращает None, если команда выполнена успешно. Он предоставляет код возврата подпроцесса в случае ошибки.

Сценарий успеха

Сценарий отказа

Мы можем использовать значение output.close() для обработки ошибок в наших скриптах Python.

Ожидают ли OS System и OS Popen завершения команды?

Прежде чем переходить к другому способу выполнения команд оболочки в Python, я хочу увидеть поведение os.system() и os.popen() с командой, выполнение которой занимает несколько секунд.

Мы будем использовать команду ping с флагом -c, чтобы остановить выполнение команды после определенного количества пакетов ECHO_RESPONSE (в данном примере 5):

Выполнение с помощью os.system

При выполнении команды с помощью os.system() мы видим, что вывод для каждой попытки проверки связи распечатывается по одной за раз, точно так же, как мы видели бы это в оболочке Linux.

Выполнение с помощью os.popen

При использовании os.popen() мы не сразу видим вывод в оболочке Python.

Вывод буферизуется, мы видим окончательный вывод только после завершения команды ping.

Функция os.popen ожидает завершения команды, прежде чем предоставить полный вывод.

Очень скоро мы рассмотрим разницу между функцией Popen модуля ОС и функцией Popen модуля подпроцесса.

Методы Read, Readline и Readlines, применяемые к выводу OS.Popen

  • os.popen() возвращает объект открытого файла.
  • мы можем прочитать содержимое объекта с помощью метода read().

В этом разделе мы сравним поведение методов read(), readline() и readlines(), применяемых к файловому объекту, возвращаемому os.popen.

Мы уже знаем, что метод чтения возвращает вывод команды, как только выполнение команды завершено.

Давайте посмотрим, что произойдет с методом readline:

С помощью метода readline мы можем печатать вывод команды по одной строке за раз, пока не достигнем конца объекта открытого файла.

Вот как можно использовать метод readline с циклом while для вывода полного вывода команды:

С другой стороны, метод readlines ожидает завершения команды и возвращает список Python:

А с помощью простого цикла for мы можем распечатать полный вывод команды, перебрав все элементы в списке, возвращаемом методом readlines():

В предыдущем разделе мы увидели, как запустить команду date, используя os.system и os.popen.

Теперь вы узнаете, как использовать модуль подпроцесса для запуска той же команды.

Давайте посмотрим, что произойдет, если я также передам флаг +%a команде даты (она должна показывать день недели):

Мы видим следующую ошибку:

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

При передаче параметра shell=True команда вызывается через оболочку.

Примечание. Помните о соображениях безопасности, связанных с использованием параметра оболочки.

Когда вы выполняете команду, функция run() ожидает ее завершения. Я объясню вам больше об этом в одном из следующих разделов.

Если мы хотим запустить команду «date +%a» без передачи shell=True, мы должны передать дату и ее флаги как отдельные элементы массива.

До сих пор мы печатали вывод команды в оболочке.

Но что, если мы хотим сохранить вывод команды в переменной?

Мы по-прежнему видим вывод команды в оболочке, а оператор печати показывает, что переменная process_output является объектом CompletedProcess.

Давайте узнаем атрибуты объекта…

Чтобы увидеть пространство имен, связанное с этим объектом Python, мы можем использовать метод __dict__.

Вы можете увидеть атрибуты, в которых хранятся аргументы, код возврата, стандартный вывод и стандартная ошибка.

Код возврата для последней выполненной команды равен нулю. Снова. код возврата для успешной команды равен нулю.

Давайте посмотрим, какой будет код возврата, если мы введем синтаксическую ошибку в команду:

Это ошибка, которую мы видим после передачи неверного флага команде даты:

Код возврата равен 1 (ненулевые коды возврата указывают на сбой).

Кроме того, стандартный вывод имеет значение None, поскольку вывод команды отправляется на терминал.

Как мы можем записать стандартный вывод в переменную?

В официальной документации подпроцесса я вижу следующее:

Итак, давайте выясним, что произойдет, если мы установим для параметра capture_output значение True…

Выполните эту команду, вы не увидите вывод команды, напечатанный в оболочке, если только вы не используете оператор печати для отображения значения переменной process_output:

  • Стандартный вывод содержит вывод команды.
  • Стандартная ошибка — пустая строка, поскольку команда даты выполнена успешно.

Давайте также подтвердим, что stderr не пуст в случае ошибки:

Ошибка сохраняется в файле process_output.stderr.

Формат стандартного вывода и стандартная ошибка команды

В последней команде мы видели, что формат stdout и stderr не очень удобочитаем.

Это потому, что они оба записываются как байты (обратите внимание на все символы новой строки в значениях stdout и stderr).

Что, если мы хотим видеть их в том же формате, что и вывод команды в оболочке?

Примечание: параметр text был введен в Python 3.7 как более понятная альтернатива параметру universal_newlines.

В качестве альтернативы вы также можете преобразовать байты в строку, используя метод декодирования:

Вы видите разницу в формате stdout с декодированием и без него?

Ранее в определении параметра capture_output мы видели, что его передача аналогична передаче stdout=PIPE и stderr=PIPE.

Давайте попробуем использовать их вместо этого, чтобы убедиться, что результат тот же…

Как видно из приведенного ниже определения, взятого из официальной документации подпроцесса, PIPE является частью модуля подпроцесса. Это означает, что мы должны использовать subprocess.PIPE в нашей программе.

Сейчас выглядит лучше 🙂

Как захватить стандартный вывод и стандартную ошибку в одном потоке

Чтобы фиксировать стандартный вывод и стандартную ошибку в одном потоке, мы должны установить для stdout значение subprocess.PIPE, а для stderr — значение subprocess.STDOUT:

Стандартный вывод содержит выходные данные, а значение стандартного вывода равно None.

А что, если при выполнении команды возникнет ошибка?

Как и ожидалось, ошибка является частью потока stdout. Значение атрибута stderr по-прежнему равно None.

Запись вывода команды в файл в Python

Вы также можете записать вывод команды в файл.

Давайте посмотрим, как использовать оператор Python with…

Обратите внимание, что на этот раз значение stdout равно None, учитывая, что мы отправляем stdout в файл.

С помощью команд ls и cat мы подтверждаем, что файл command.out создан и содержит вывод команды, выполненной в нашей программе Python.

Как насчет того, чтобы записать стандартную ошибку в файл?

Для этого мы можем открыть два файла с помощью оператора Python with.

На этот раз и для stdout, и для stderr задано значение None, и при вызове команды создаются два файла (файл command.err пуст, поскольку команда выполнена успешно).

Прежде чем продолжить, попробуйте выполнить команду с неправильным синтаксисом и убедитесь, что ошибка записана в файл command.err.

Перенаправить вывод команды в /dev/null

Возможно, вам потребуется перенаправить вывод команды в /dev/null.

Для этого мы можем использовать специальное значение, предоставленное модулем подпроцесса: DEVNULL.

Как вызвать исключение Python при сбое команды оболочки

В одном из предыдущих примеров мы видели, что произойдет, если мы запустим команду с неправильным синтаксисом:

Ошибка сохраняется в потоке stderr, но Python не создает никаких исключений.

Python вызывает исключение subprocess.CalledProcessError, которое мы можем перехватить как часть блока try и exclude.

Теперь мы можем лучше обрабатывать ошибки:

Как запустить несколько команд с подпроцессом

В Linux очень часто используется конвейер для отправки вывода команды в качестве ввода другой команды.

Мы увидим, как можно сделать то же самое в Python с помощью подпроцесса.

Мы выполним первую команду так же, как делали это раньше, а затем выполним вторую команду, которая получит дополнительный входной параметр.

В качестве значения input будет установлено значение stdout первой команды.

Легче показать это на примере...

И я хочу запустить следующую команду в Python:

Нам нужно взять вывод команды wc и передать его в качестве ввода команды awk.

Мы также передаем входной параметр для выполнения второй команды (awk), и его значение устанавливается равным стандартному выводу первой команды (wc).

Конечный результат:

Shlex.split и модуль подпроцесса

Для длинной команды создавать этот список вручную может быть утомительно. Решением этой проблемы является модуль shlex, а именно функция разделения.

В качестве примера возьмем команду wc, которую мы использовали в предыдущем разделе:

Вот что происходит, когда вы применяете shlex.split к этой строке:

Пришло время запустить нашу команду с помощью shlex.split:

Печать переменных среды оболочки в Python

Возможно, вы захотите использовать переменные среды оболочки в своей программе Python.

Давайте узнаем, как это сделать…

Наша программа не разрешает значение переменной окружения $SHELL. Для этого мы должны использовать os.path.expandvars("$SHELL").

Использование подпроцесса с SSH

Вы также можете использовать подпроцесс для выполнения команд в удаленной системе через SSH.

Посмотрите, как мы используем стандартный ввод для вызова команды date через SSH.

А вот вывод скрипта:

Вот что говорится в официальной документации о функции call…

Проверим, правда ли это…

Но я получаю сообщение об ошибке:

Это потому, что из subprocess.call мы получаем не объект, а только целое число для кода возврата:

Глядя на документацию subprocess.call, я заметил следующее сообщение:

Как тогда мы можем получить вывод subprocess.call?

Но потом я понял, что и в этом случае документация предлагает использовать run().

Причина, по которой я хочу показать вам это, заключается в том, что в поведении subprocess.Popen есть что-то довольно интересное.

Теперь давайте запустим ту же команду, используя subprocess.Popen…

Чтобы получить стандартный вывод и стандартную ошибку, мы должны использовать функцию connect(), которая возвращает кортеж, в котором первым элементом является стандартный вывод, а вторым элементом – стандартный вывод.

Вернемся к тому факту, что subprocess.Popen выполнялся немедленно (неблокирующим образом) в оболочке Python, даже если команда ping не завершалась немедленно.

С помощью subprocess.Popen мы можем опросить статус долго выполняющейся команды, вот как:

Функция poll() возвращает None во время выполнения команды.

Это можно использовать для выхода из цикла while только после завершения выполнения команды, поскольку код возврата не равен None.

Заключение

Мы видели множество различных способов выполнения команды оболочки в Python.

Молодцы, что закончили это руководство!

Чтобы привыкнуть к синтаксису модуля подпроцесса, требуется практика, поэтому обязательно попробуйте примеры, которые мы рассмотрели в этом руководстве, на своем компьютере.

Если я просто использую print в Python и запускаю его в терминале, будет ли он выполняться так же, как если бы вы вводили его самостоятельно и нажимали Enter ?

6 ответов 6

Вы можете использовать os.system() , например:

Или в вашем случае:

Более того, вы можете использовать вызов подпроцесса, это безопаснее, мощнее и, вероятно, быстрее:

Или без вызова оболочки:

Если вы хотите зафиксировать выходные данные, один из способов сделать это так:

Я настоятельно рекомендую установить тайм-аут в сообщении, а также зафиксировать исключения, которые вы можете получить при его вызове. Это очень подверженный ошибкам код, поэтому вы должны ожидать возникновения ошибок и обрабатывать их соответствующим образом.

@binarysubstrate устарел, так как не поддерживается или недоступен? Недавно я работал на машине с 2.7 (не по своему выбору), и os.system все еще работает.

В Python 3.4 необходимо указать shell=True, иначе команда call не будет работать. По умолчанию вызов попытается открыть файл, указанный в строке, если не установлено значение shell=True. Также похоже, что в Python 3.5 call заменен на run

Первая команда просто записывает в файл. Вы бы не выполнили это как команду оболочки, потому что python может читать и записывать файлы без помощи оболочки:

Команду iptables можно выполнять извне. Лучший способ сделать это — использовать модуль подпроцесса.

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

Самый быстрый способ:

Это не самый гибкий подход; если вам нужен больший контроль над вашим процессом, чем «запустить его один раз, до завершения и заблокировать, пока он не завершится», вам следует вместо этого использовать модуль подпроцесса.

Как правило, лучше использовать привязки Python, когда это возможно (помимо других преимуществ, лучше перехватывать исключения).

Для команды echo очевидно, что лучше использовать python для записи в файл, как это предлагается в ответе @jordanm.

Для команды iptables, возможно, python-iptables (страница PyPi, страница GitHub с описанием и документацией) предоставит то, что вам нужно (я не проверял вашу конкретную команду).

Это заставит вас зависеть от внешней библиотеки, поэтому вам придется взвешивать преимущества. Использование подпроцесса работает, но если вы хотите использовать выходные данные, вам придется самостоятельно анализировать их и обрабатывать изменения вывода в будущих версиях iptables.

Повторяющиеся задачи созрели для автоматизации. Разработчики и системные администраторы обычно автоматизируют рутинные задачи, такие как проверка работоспособности и резервное копирование файлов, с помощью сценариев оболочки. Однако по мере того, как эти задачи становятся более сложными, может стать сложнее поддерживать сценарии оболочки.

К счастью, для автоматизации мы можем использовать Python вместо сценариев оболочки. Python предоставляет методы для запуска команд оболочки, предоставляя нам ту же функциональность, что и сценарии этих оболочек. Изучение того, как выполнять команды оболочки в Python, открывает перед нами двери для автоматизации компьютерных задач структурированным и масштабируемым способом.

В этой статье мы рассмотрим различные способы выполнения команд оболочки в Python и идеальные ситуации для использования каждого метода.

Использование os.system для запуска команды

Python позволяет нам немедленно выполнить команду оболочки, которая хранится в строке, с помощью функции os.system().

Давайте начнем с создания нового файла Python с именем echo_adelle.py и введите следующее:

Первое, что мы делаем в нашем файле Python, — это импортируем модуль os, который содержит системную функцию, которая может выполнять команды оболочки. Следующая строка делает именно это, запускает команду echo в нашей оболочке через Python.

В терминале запустите этот файл с помощью следующей команды, и вы должны увидеть соответствующий вывод:

Поскольку эхо-команды выводят данные на наш стандартный вывод, os.system() также отображает выходные данные в нашем потоке стандартного вывода. Хотя команда os.system() не отображается в консоли, она возвращает код выхода команды оболочки. Код выхода 0 означает, что он запустился без проблем, а любое другое число означает ошибку.

Давайте создадим новый файл с именем cd_return_codes.py и введите следующее:

В этом сценарии мы создаем две переменные, в которых хранятся результаты выполнения команд, меняющих каталог на домашнюю папку и на несуществующую папку. Запустив этот файл, мы увидим:

Первая команда, которая меняет каталог на домашний, выполняется успешно. Поэтому os.system() возвращает свой код выхода, ноль, который хранится в home_dir. С другой стороны, unknown_dir хранит код выхода неудачной команды bash для изменения каталога на несуществующую папку.

Функция os.system() выполняет команду, выводит все выходные данные команды на консоль и возвращает код выхода команды. Если нам нужен более детальный контроль над вводом и выводом команды оболочки в Python, мы должны использовать модуль подпроцесса.

Выполнение команды с подпроцессом

Модуль подпроцесса — это рекомендуемый Python способ выполнения команд оболочки. Это дает нам возможность подавлять вывод команд оболочки или связывать входные и выходные данные различных команд вместе, обеспечивая при этом аналогичный os.system() опыт для основных случаев использования.

В новом файле с именем list_subprocess.py напишите следующий код:

Примечание. Как правило, аргументы нужно разделять по пространству, например, ls -alh будет ["ls", "-alh"] , а ls -a -l -h – ["ls", "-a", -"l", "-h"] . В качестве другого примера, echo hello world будет ["echo", "hello", "world"] , тогда как echo "hello world" или echo hello\ world будет ["echo", "hello world"] .

Запустите этот файл, и вывод вашей консоли будет похож на следующий:

Бесплатная электронная книга: Git Essentials

Ознакомьтесь с нашим практическим руководством по изучению Git, включающим передовые практики, общепринятые стандарты и памятку. Перестаньте гуглить команды Git и на самом деле изучите их!

Стандартный вывод команды теперь направляется на специальное устройство /dev/null, что означает, что вывод не будет отображаться на наших консолях. Запустите файл в вашей оболочке, чтобы увидеть следующий вывод:

  • stdout=subprocess.PIPE указывает Python перенаправить вывод команды в объект, чтобы его можно было прочитать вручную позже
  • text=True возвращает stdout и stderr в виде строк. Тип возвращаемого значения по умолчанию — байты.
  • input="Привет с другой стороны" указывает Python добавить строку в качестве входных данных для команды cat.

Запуск этого файла приводит к следующему выводу:

Мы также можем вызвать исключение, не проверяя возвращаемое значение вручную. В новый файл false_subprocess.py добавьте следующий код:

В терминале запустите этот файл. Вы увидите следующую ошибку:

Используя check=True , мы указываем Python вызывать любые исключения в случае возникновения ошибки. Поскольку мы столкнулись с ошибкой, оператор печати в последней строке не был выполнен.

Выполнение команды с помощью Popen

Класс subprocess.Popen предоставляет разработчику больше возможностей при взаимодействии с оболочкой. Однако нам нужно более четко указывать результаты и ошибки.

По умолчанию subprocess.Popen не останавливает обработку программы Python, если ее команда не завершила выполнение. В новом файле с именем list_popen.py введите следующее:

Этот код эквивалентен коду list_subprocess.py . Он запускает команду с помощью subprocess.Popen и ждет ее завершения перед выполнением остальной части скрипта Python.

Допустим, мы не хотим ждать, пока наша команда оболочки завершит выполнение, чтобы программа могла работать над другими задачами. Как он узнает, что команда оболочки завершила выполнение?

Метод poll() возвращает код выхода, если команда завершила выполнение, или None, если она еще выполняется. Например, если бы мы хотели проверить, завершен ли list_dir, а не ждать его завершения, у нас была бы следующая строка кода:

Чтобы управлять вводом и выводом с помощью subprocess.Popen , нам нужно использовать методcommunication().

В новый файл с именем cat_popen.py добавьте следующий фрагмент кода:

Методcommunication() принимает входной аргумент, который используется для передачи входных данных команде оболочки. Метод сообщения также возвращает как stdout, так и stderr, если они установлены.

Рассмотрев основные идеи, лежащие в основе subprocess.Popen , мы рассмотрели три способа запуска команд оболочки в Python. Давайте еще раз изучим их характеристики, чтобы узнать, какой метод лучше всего подходит для требований проекта.

Какой из них следует использовать?

Вот таблица с некоторыми различиями в удобстве использования, которую вы также можете использовать при принятии решения:

Python — это предпочтительный язык для написания сценариев оболочки и автоматизации задач. Он популярен в системном администрировании, потому что может выполнять команды оболочки, используя только библиотеки по умолчанию. Существует два способа запуска команд Linux с помощью Python: с помощью модуля os и с помощью модуля подпроцесса.

В этом руководстве мы увидим, как запускать команды оболочки Linux с Python, используя модули os и subprocess на Raspberry Pi.

Использование модуля ОС

Во-первых, это модуль os. Согласно официальной документации, модуль os предоставляет портативный способ использования функций, зависящих от операционной системы. Разве это не удобно? С короткими кодами Python вы уже можете выполнять стандартные задачи операционной системы, не взаимодействуя с интерфейсом рабочего стола. Системный метод позволяет сделать именно это. Чтобы использовать его для запуска команды Linux, ваш код должен выглядеть так, как показано ниже.

Пример кода с использованием system()

Эта 4-строчная проверка проверяет ваш текущий каталог, изменяет местоположение на ваш домашний каталог и подробно перечисляет все содержимое. Это довольно простая реализация, но есть и обратная сторона. С помощью system() вам не разрешено сохранять результирующий вывод в виде переменной.

Вместо этого вы можете использовать метод popen(), который все еще находится в модуле os. Он открывает канал из или в командную строку. Канал соединяет вывод одной команды с вводом другой команды. Это делает его доступным в Python. Чтобы использовать popen() для сохранения в качестве переменной, см. приведенный ниже пример кода.

Пример кода с использованием popen()

Если вы напечатаете переменную потока, вы увидите возвращаемые ею данные. Он состоит из фактически выполненных команд, режима и адреса. Кроме того, если вы хотите получить весь вывод в виде одной строки, измените readlines() на read() .

Использование модуля подпроцесса

Второй способ запуска команд Linux с помощью Python — использование более нового модуля подпроцесса. Этот модуль позволяет создавать новые процессы, подключаться к их каналам ввода/вывода/ошибок и получать их коды возврата. Он был создан для замены функций os.system() и os.popen().

Единственный метод, который имеет значение в подпроцессе, — это run() . С его помощью вы можете делать все, что мы делали выше, и даже больше, используя различные аргументы. Используйте следующие коды для справки:

Написание простой команды с использованием подпроцесса

Написание команды с переключателями

Далее, чтобы сохранить вывод команды внутри переменной, просто сделайте это так же, как и любые другие данные. Однако результат будет не таким, как вы ожидаете. Поскольку основная цель «выполнить» — выполнить команду оболочки в python, результат не будет похож на вывод, который вы видите в терминале. Это будут возвращаемые данные, как в os.open. Вы можете проверить это, используя приведенный ниже код.

Сохранение вывода команды в переменной

Этот скетч анализирует возвращаемые данные вашей команды, используя аргументы метода. Вот некоторые из часто используемых:

  • args — возвращает фактически выполненные команды
  • returncode — возвращает код возврата вывода; 0 означает отсутствие ошибок
  • stdout — захвачен стандартный вывод из дочернего процесса
  • stderr — захваченный поток stderr из дочернего процесса

Поскольку мы не захватили вывод предыдущего кода, мы получим «none» с аргументами stdout и stderr. Чтобы включить аргумент вывода захвата, обратитесь к следующему коду:

Если вы напечатаете x, вы получите список элементов в вашем текущем каталоге с типом bytes. Преобразуйте его в строку, написав x.stdout.decode() . В качестве альтернативы вы можете передать аргумент text=True с основной функцией. Вывод теперь должен выглядеть точно так же, как и в терминале.

Наконец, мы будем запускать команды Linux с помощью Python и сохранять вывод, который вы видите в терминале, в текстовый файл — простая задача с подпроцессом. Вам просто нужно перенаправить поток stdout в текстовый файл, используя аргумент stdout .

Сохранение вывода команды в текстовый файл

Это урок! Подводя итог, для быстрого выполнения короткого сценария рассмотрите возможность использования методов os.system() и os.popen(). Но если у вас есть более сложный сценарий для запуска, вы можете вместо этого использовать модуль подпроцесса.

Спасибо за чтение и не забудьте оставить комментарий ниже, если у вас есть какие-либо вопросы!

Читайте также: