Python linux subprocess call

Как запускать внешние процессы, используя Python и модуль subprocess

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

Прочитав статью, вы узнаете как:

  • Использовать функцию run для запуска внешнего процесса.
  • Получить стандартный вывод процесса и информацию об ошибках.
  • Проверить код возврата процесса и вызвать исключение в случае сбоя.
  • Запустить процесс, используя оболочку в качестве посредника.
  • Установить время ожидания завершения процесса.
  • Использовать класс Popen напрямую для создания конвейера ( pipe ) между двумя процессами.

Так как модуль subprocess почти всегда используют с Linux все примеры будут касаться Ubuntu. Для пользователей Windows советую скачать терминал Ubuntu 18.04 LTS.

Функция «run»

Функция run была добавлена в модуль subprocess только в относительно последних версиях Python (3.5). Теперь ее использование является рекомендуемым способом создания процессов и должно решать наиболее распространенные задачи. Прежде всего, давайте посмотрим на простейший случай применения функции run .

Предположим, мы хотим запустить команду ls -al ; для этого в оболочке Python нам нужно ввести следующие инструкции:

Вывод внешней команды ls отображается на экране:

Здесь мы просто использовали первый обязательный аргумент функции run , который может быть последовательностью, «описывающей» команду и ее аргументы (как в примере), или строкой, которая должна использоваться при запуске с аргументом shell=True (мы рассмотрим последний случай позже).

Захват вывода команды: stdout и stderr

Что, если мы не хотим, чтобы вывод процесса отображался на экране. Вместо этого, нужно чтобы он сохранялся: на него можно было ссылаться после выхода из процесса? В этом случае нам стоит установить для аргумента функции capture_output значение True :

Как мы можем впоследствии получить вывод ( stdout и stderr ) процесса? Если вы посмотрите на приведенные выше примеры, то увидите, что мы использовали переменную process для ссылки на объект CompletedProcess , возвращаемый функцией run . Этот объект представляет процесс, запущенный функцией, и имеет много полезных свойств. Помимо прочих, stdout и stderr используются для «хранения» соответствующих дескрипторов команды, если, как уже было сказано, для аргумента capture_output установлено значение True . В этом случае, чтобы получить stdout , мы должны использовать:

По умолчанию stdout и stderr представляют собой последовательности байтов. Если мы хотим, чтобы они хранились в виде строк, мы должны установить для аргумента text функции run значение True .

Управление сбоями процесса

Команда, которую мы запускали в предыдущих примерах, была выполнена без ошибок. Однако при написании программы следует принимать во внимание все случаи. Так, что случится, если порожденный процесс даст сбой? По умолчанию ничего «особенного» не происходит. Давайте посмотрим на примере: мы снова запускаем команду ls , пытаясь вывести список содержимого каталога /root, который не доступен для чтения обычным пользователям:

Мы можем узнать, не завершился ли запущенный процесс ошибкой, проверив его код возврата, который хранится в свойстве returncode объекта CompletedProcess :

Видите? В этом случае returncode равен 2 , подтверждая, что процесс столкнулся с ошибкой, связанной с недостаточными правами доступа, и не был успешно завершен. Мы могли бы проверять выходные данные процесса таким образом чтобы при возникновении сбоя возникало исключение. Используйте аргумент check функции run : если для него установлено значение True , то в случае, когда внешний процесс завершается ошибкой, возникает исключение CalledProcessError :

Обработка исключений в Python довольно проста. Поэтому для управления сбоями процесса мы могли бы написать что-то вроде:

Исключение CalledProcessError , как мы уже сказали, возникает, когда код возврата процесса не является 0 . У данного объекта есть такие свойства, как returncode , cmd , stdout , stderr ; то, что они представляют, довольно очевидно. Например, в приведенном выше примере мы просто использовали свойство cmd , чтобы отобразить последовательность, которая использовалась для запуска команды при возникновении исключения.

Выполнение процесса в оболочке

Процессы, запущенные с помощью функции run , выполняются «напрямую», это означает, что для их запуска не используется оболочка: поэтому для процесса не доступны никакие переменные среды и не выполняются раскрытие и подстановка выражений. Давайте посмотрим на пример, который включает использование переменной $HOME :

Как видите, переменная $HOME не была заменена на соответствующее значение. Такой способ выполнения процессов является рекомендованным, так как позволяет избежать потенциальные угрозы безопасности. Однако, в некоторых случаях, когда нам нужно вызвать оболочку в качестве промежуточного процесса, достаточно установить для параметра shell функции run значение True . В таких случаях желательно указать команду и ее аргументы в виде строки:

Все переменные, существующие в пользовательской среде, могут использоваться при вызове оболочки в качестве промежуточного процесса. Хотя это может показаться удобным, такой подход является источником проблем. Особенно при работе с потенциально опасным вводом, который может привести к внедрению вредоносного shell-кода. Поэтому запуск процесса с shell=True не рекомендуется и должен использоваться только в безопасных случаях.

Ограничение времени работы процесса

Обычно мы не хотим, чтобы некорректно работающие процессы бесконечно исполнялись в нашей системе после их запуска. Если мы используем параметр timeout функции run , то можем указать количество времени в секундах, в течение которого процесс должен завершиться. Если он не будет завершен за это время, процесс будет остановлен сигналом SIGKILL. Который, как мы знаем, не может быть перехвачен. Давайте продемонстрируем это, запустив длительный процесс и предоставив timeout в секундах:

В приведенном выше примере мы запустили команду ping без указания фиксированного числа пакетов ECHO REQUEST, поэтому она потенциально может работать вечно. Мы также установили время ожидания в 5 секунд с помощью параметра timeout . Как мы видим, ping была запущена, а по истечении 5 секунд возникло исключение TimeoutExpired и процесс был остановлен.

Читайте также:  Прошивка модема под windows

Функции call, check_output и check_call

Как мы уже говорили ранее, функция run является рекомендуемым способом запуска внешнего процесса. Она должна использоваться в большинстве случаев. До того, как она была представлена в Python 3.5, тремя основными функциями API высокого уровня, применяемыми для создания процессов, были call , check_output и check_call ; давайте взглянем на них вкратце.

Прежде всего, функция call : она используется для выполнения команды, описанной параметром args; она ожидает завершения команды; ее результатом является соответствующий код возврата. Это примерно соответствует базовому использованию функции run.

Поведение функции check_call практически не отличается от run , когда для параметра check задано значение True : она запускает указанную команду и ожидает ее завершения. Если код возврата не равен 0 , возникает исключение CalledProcessError .

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

Работа на более низком уровне с классом Popen

До сих пор мы изучали функции API высокого уровня в модуле subprocess, особенно run . Все они под капотом используют класс Popen . Из-за этого в подавляющем большинстве случаев нам не нужно взаимодействовать с ним напрямую. Однако, когда требуется большая гибкость, без создания объектов Popen не обойтись.

Предположим, например, что мы хотим соединить два процесса, воссоздав поведение конвейера (pipe) оболочки. Как мы знаем, когда передаем две команды в оболочку, стандартный вывод той, что находится слева от пайпа «|», используется как стандартный ввод той, которая находится справа. В приведенном ниже примере результат выполнения двух связанных конвейером команд сохраняется в переменной:

Чтобы воссоздать подобное поведение с помощью модуля subprocess без установки параметра shell в значение True , как мы видели ранее, мы должны напрямую использовать класс Popen :

Источник

10+ practical examples to learn python subprocess module

Table of Contents

Running and spawning a new system process can be useful to system administrators who want to automate specific operating system tasks or execute a few commands within their scripts. Python provides many libraries to call external system utilities, and it interacts with the data produced. In this tutorial we will learn about one such python subprocess() module

The first library that was created is the OS module, which provides some useful tools to invoke external processes, such as os.system , os.spwan , and os.popen* . It lacks some essential functions, however, so Python developers have introduced the subprocess module which is intended to replace functions such as os.system() , os.spawnv() , the variations of popen() in the os , popen2 modules, and the commands module.

Content of python subprocess module

Access contents of python subprocess() module

Output from this script:

Using python subprocess.Popen() function

  • Your Python program can start other programs on your computer with the Popen() function in the built-in subprocess module.
  • The P in the name of the Popen() function stands for process.
  • If you have multiple instances of an application open, each of those instances is a separate process of the same program.
  • For example, if you open multiple windows of your web browser at the same time, each of those windows is a different process of the web browser program
Arguments Meaning
args A string, or a sequence of program arguments.
bufsize It is supplied as the buffering argument to the open() function when creating the stdin/stdout/stderr pipe file objects.
executable A replacement program to execute.
stdin, stdout, stderr These specify the executed program’s standard input, standard output, and standard error file handles, respectively.
shell If True , the command will be executed through the shell (the default is False ). In Linux, this means calling the /bin/sh before running the child process.
cwd Sets the current directory before the child is executed.
env Defines the environmental variables for the new process.

The general syntax to use subprocess.Popen

In this syntax we are storing the command output ( stdout ) and command error ( stderr ) in the same variable i.e. sp

This is a very basic example where we execute » ls -ltr » using python subprocess, similar to the way one would execute it on a shell terminal

The output from this script:

Did you observe the last line » «, this is because we are not storing the output from the system command and instead just printing it on the console.

Reading stdin, stdout, and stderr with python subprocess.communicate()

In subprocess , Popen() can interact with the three channels and redirect each stream to an external file, or to a special value called PIPE . An additional method, called communicate() , is used to read from the stdout and write on the stdin .

The spawned processes can communicate with the operating system in three channels:

  • Standard input ( stdin )
  • Standard output ( stdout )
  • Standard error ( stderr )

The communicate() method can take input from the user and return both the standard output and the standard error, as shown in the following code snippet:

In this code if you observe we are storing the STDOUT and STDERR into the sp variable and later using communicate() method, we separate the output and error individually into two different variables

The output from this script:

  • As you observe, the return code is 0 which means the command was executed successfully
  • But the output is not clear, because by default file objects are opened in binary mode. Observe the ‘b ‘ in the starting of output which means the output is in byte code, we will get to this later.
  • The error code is also empty, this is again because our command was successful.

Convert bytes to string

There are a couple of methods you can use to convert the byte into string format for subprocess.Popen output:

  • decode(«utf-8»)
  • universal_newlines
  • text (This is supported with Python 3.7+, text was added as a more readable alias for universal_newlines )

We will use universal_newlines=True in our script

and now the script output is more readable:

Difference between shell=True or shell=False, which one to use?

Using subprocess.Popen with shell=True

  • Whenever we use shell=True , Python is going to open a new shell to execute the provided command.
  • The benefit of using this is that you can give the command in plain text format and Python will execute the same in the provided format.
  • This can be useful for Linux system commands

In this python code, I am just trying to list the content of current directory using » ls -lrt » with shell=True

Now if we execute our script:

The output is similar to what we get when we manually execute » ls -lrt » from the shell terminal. Now let me intentionally fail this script by giving some wrong command, and then the output from our script:

  • In our script I changed ‘ ls ‘ to ‘ lsss ‘ to intentionally fail the script.
  • Now the script has an empty output under » output is: «
  • The return code is non zero which means error
  • While the error contains the error output from the provided command

Using subprocess.Popen with shell=False

In this section we will use shell=False with python subprocess.Popen to understand the difference with shell=True
So if you define shell=True , you are asking Python to execute your command under a new shell but with shell=False you must provide the command in List format instead of string format as we did earlier.

So for example we used below string for shell=True

Now to be able to use this command with shell=False , we must convert into List format, this can be done manually:

Or if it is too complex for you, use split() method (I am little lazy) which should convert the string into list

and this should convert your command into string which can be further used with shell=False

So let us take the same example, and convert the command into list format to be able to use with Python subprocess and shell=False

The output from this script:

When should I use shell=True or shell=False?

Now you must be wondering, when should I use which method? As ultimately both seem to be doing the same thing, one way or the other. We will understand this in our next example.

In this example script, we will try to use our system environment variable with shell=True

Output from this script is as expected, it is printing our PATH variable content

Now let us try to get the same using shell=False

Here the script output will just print $PATH variable as a string instead of the content of $PATH variable.

  • So whenever you are planning to use any system variable, you must use shell=True as shell=False will not be able to interpret the system variable and would consider it as just another string.
  • Also one of the demerit of shell=True method is that, every time you use this method, Python needs to open a new shell terminal to execute the command, get the status and then close the terminal which takes up some additional time while with shell=False , you can get comparatively faster results.

So what did we learned? To summarise:

  • If shell=True then your command is considered as a string (similar to your os command)
  • If shell=False then your command is considered as a List
  • To convert a string with shell=False , you can use split() method for easier operation
  • shell=False doesn’t recognise OS system environment variables

Practical Example

Let us take a practical example from real time scenario. In this python script we aim to get the list of failed services. In RHEL 7/8 we use » systemctl —failed » to get the list of failed services

Now here I only wish to get the service name, instead of complete output.

Below is our sample script:

Here,
Line 3: We import subprocess module
Line 6: We define the command variable and use split() to use it as a List
Line 9: Print the command in list format, just to be sure that split() worked as expected
Line 12: The subprocess.Popen command to execute the command with shell=False . Store the output and error, both into the same variable. Return the output with newline char instead of byte code
Line 15: This may not be required here but it is a good practice to use wait() as sometimes the subprocess may take some time to execute a command for example some SSH process, in such case wait() will make sure the subprocess command is executed successfully and the return code is stored in wait()
Line 19: We need communicate() to get the output and error value from subprocess.Popen and store it in out and err variable respectively
Line 21: If return code is 0 , i.e. if the command execution was success
Line 22: Use for loop and run on each line. Here we will use python splitlines() method which splits the string based on the lines. It breaks the string at line boundaries and returns a list of splitted strings.
Line 23: Use » in » operator to search for » failed » string in » line »
Line 24: If » failed » is found in the » line »
Line 25: The split the found line into list and then we print the content of string with » 1 » index number
Line 25: In case the command fails to execute
Line 26: Print the provided error message from err variable which we stored using communicate()

Output from this script:

So we were able to print only the failed service using python subprocess module

Using python subprocess.call() function

  • The subprocess module provides another function that makes process spawning a safer operation than using Popen() .
  • The subprocess.call() function waits for the called command/program to finish reading the output.
  • It supports the same arguments as the Popen() constructor, such as shell, executable, and cwd , but this time, your script will wait for the program to complete and populate the return code without the need to communicate() .
  • You can fetch the exit status using Popen.returncode
  • To suppress stdout or stderr , supply a value of subprocess.DEVNULL which indicates that the special file os.devnull will be used.
  • In this sample python code, we will check the availability of eth0 interface using » ip link show eth0 «
  • The output of the command will be stored in /tmp/dataFile and if eth0 is available we print » Yes, eth0 is available on this server «
  • For error condition also, the output of » ip link show eth0 » will be written in /tmp/dataFile , you can choose to use another file for error output.

The output from this script (when eth0 is available)

Content of /tmp/dataFile

The output from this script (when eth0 is NOT available)

Content of /tmp/dataFile

Using python subprocess.check_call() function

  • This is another function which is part of subprocess module which can run command with arguments.
  • check_call will wait for command execution to complete.
  • If the execution is successful then the function will return zero then return, otherwise raise CalledProcessError .
  • The CalledProcessError object will have the return code in the returncode attribute.

I will try to use subprocess.check_now just to print the command execution output:

The output from this script (when returncode is zero):

The output from this script (when returncode is non-zero):

As you see we get subprocess.CalledProcessError for non-zero return code.

So we should use try and except for subprocess.check_now as used in the below code:

The output from this script (when returncode is non-zero):

So now this time we don’t get CalledProcessError , instead the proper stderr output along with out print statement is printed on the console

Using subprocess.run() function

  • The subprocess.run() function was added in Python 3.5
  • Wait for command to complete, then return a subprocess.CompletedProcess instance.
  • The full function signature is largely the same as that of the Popen constructor — most of the arguments to this function are passed through to that interface. ( timeout , input , check , and capture_output are not.)
  • If capture_output is true, stdout and stderr will be captured.
  • When used, the internal Popen object is automatically created with stdout=PIPE and stderr=PIPE .
  • The stdout and stderr arguments may not be supplied at the same time as capture_output .
  • If you wish to capture and combine both streams into one, use stdout=PIPE and stderr=STDOUT instead of capture_output .

In this sample python code we will try to check internet connectivity using subprocess.run()

For successful output:

For failed output i.e. when the command returns non-zero exit code:

You can use check=false if you don’t want to print any ERROR on the console, in such case the output will be:

Using python subprocess.check_output() function

  • The subprocess.check_output() is similar to subprocess.run(check=True)
  • By default, this function will return the data as encoded bytes so you can use text=True or universal_newlines=True to get string value as output
  • This function will run command with arguments and return its output.
  • If the return code was non-zero it raises a CalledProcessError . The CalledProcessError object will have the return code in the returncode attribute and any output in the output attribute.

Output from the script for non-zero exit code:

Which subprocess module function should I use?

  • The functions run() , call() , check_call() , and check_output() are wrappers around the Popen class.
  • Using Popen directly gives more control over how the command is run, and how its input and output streams are processed.

Wait for the command to complete

  • Use subprocess.call or subprocess.run to run the command described by args. Wait for command to complete, then return the returncode attribute.
  • Use subprocess.Popen with wait() to wait for the command to complete

Here we use subprocess.call to check internet connectivity and then print » Something «

Output from this script:

So we know subprocess.call is blocking the execution of the code until cmd is executed. Now we do the same execution using Popen (without wait()) .

If you observe, » Something » was printed immediately while ping was still in process, so call() and run() are non-blocking function.

Capture output from command

  • The standard input and output channels for the process started by call() are bound to the parent’s input and output.
  • That means the calling program cannot capture the output of the command. Use check_output() to capture the output for later processing.
  • To prevent error messages from commands run through check_output() from being written to the console, set the stderr parameter to the constant STDOUT .

Error Handling

  • The call() function returns the program’s exit code, which is an integer that has a program-defined meaning, which is usually used to know whether the program has succeeded or failed.
  • The check_call() function works like call() , except that the exit code is checked, and if it indicates an error happened, then a CalledProcessError exception is raised.
  • The check_output() function returns whatever text the program printed and raises an exception if the program exited with a non-zero exit code. The raised exception has an attribute called output that contains the text output of the program. So, even if the program exited with an error code, we could still get the output if we want it.

Conclusion

In this tutorial we learned about different functions available with python subprocess module and their usage with different examples. As a Linux administrator coming from shell background, I was using mostly os module which now I have switched to subprocess module as this is the preferred solution to execute system commands and child processes.

In most cases you will end up using subprocess.Popen() or subprocess.run() as they tend to cover most of the basic scenarios related to execution and checking return status but I have tried to give you a comprehensive overview of possible use cases and the recommended function so you can make an informed decision.

Lastly I hope this tutorial on python subprocess module in our programming language section was helpful. So, let me know your suggestions and feedback using the comment section.

References

Didn’t find what you were looking for? Perform a quick search across GoLinuxCloud

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

For any other feedbacks or questions you can either use the comments section or contact me form.

Thank You for your support!!

Источник

Читайте также:  Windows server standard active directory
Оцените статью