Что такое подпроцесс в Python? [5 Usage Examples]

Что такое подпроцесс в Python? [5 Usage Examples]

Подпроцессы позволяют взаимодействовать с операционной системой на совершенно новом уровне.

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

Хорошая вещь в этом заключается в том, что каждое действие, которое мы делаем на нашем компьютере, включает в себя вызов подпроцесса. Это остается верным, даже если мы напишем простой скрипт «hello world» на Python.

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

К концу этого руководства вы:

  • Понимание концепции подпроцессов
  • Вы изучили основы библиотеки подпроцессов Python.
  • Практикуйте свои навыки Python на полезных примерах

Давайте углубимся в это

Концепция подпроцесса

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

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

Есть несколько способов визуализировать процесс, происходящий на нашем компьютере. Например, в UNIX (Linux и MAC) у нас есть htop, интерактивный просмотрщик процессов.

Режим дерева — наиболее полезный инструмент для просмотра запущенных подпроцессов. Мы можем активировать его с помощью F5.

Если мы внимательно посмотрим на раздел команд, то сможем заметить структуру процессов, запущенных на нашем компьютере.

Все начинается с /sbin/init — команды, запускающей каждый процесс на нашем компьютере. С этого момента мы можем видеть запуск других процессов, таких как kfce4-screenshoter и kfce4-terminal (что приводит к еще большему количеству подпроцессов).

Если мы посмотрим на Windows, у нас есть мифический диспетчер задач, который полезен при уничтожении этих программ на нашей машине.

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

Подпроцессы в Python

Подпроцесс в Python — это задача, которую сценарий Python делегирует операционной системе (ОС).

Библиотека подпроцессов позволяет нам выполнять подпроцессы и управлять ими непосредственно из Python. Сюда входит работа со стандартным вводом stdin, стандартным выводом stdout и кодами возврата.

Нам не нужно устанавливать его с помощью PIP, так как частью Python является стандартная библиотека.

Поэтому мы можем начать использовать подпроцессы в Python, просто импортировав модуль.

import subprocess

# Using the module ....

Примечание. Чтобы следовать этой статье, у вас должен быть Python 3.5+.

Чтобы проверить текущую версию Python, просто запустите ее.

❯ python --version
Python 3.9.5 # My result

Если вы получаете версию Python 2.k, вы можете использовать следующую команду

python3 --version

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

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

Давайте посмотрим, как его использовать, создав простой скрипт, в котором перечислены файлы текущего каталога.

Первое применение подпроцесса

Во-первых, давайте создадим файл list_dir.pi. Это будет файл, в котором мы будем экспериментировать со списком файлов.

touch list_dir.py

Теперь давайте откроем этот файл и используем следующий код.

import subprocess 

subprocess.run('ls')

Сначала мы импортируем модуль подпроцесса, а затем, используя функцию запуска для запуска, команду, которую мы передаем в качестве аргумента.

Эта функция была представлена ​​в Python 3.5 как удобный ярлык для subprocess.Popen. Функция subprocess.run позволяет нам запустить команду и дождаться ее завершения, в отличие от Popen, где у нас есть возможность вызвать функцию коммуникации позже.

Говоря о выводе кода, ls — это команда UNIX, которая выводит список файлов в каталоге, в котором вы находитесь. Поэтому, если вы запустите эту команду, вы получите список файлов, присутствующих в текущем каталоге.

❯ python list_dir.py
example.py  LICENSE  list_dir.py  README.md

Примечание. Обратите внимание, что если вы работаете в Windows, вам нужно будет использовать другие команды. Например, вместо «ls» вы можете использовать «dir».

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

Например, чтобы перечислить как скрытые файлы (те, которые начинаются с точки), так и все метаданные файлов, мы пишем следующий код.

import subprocess

# subprocess.run('ls')  # Simple command

subprocess.run('ls -la', shell=True)

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

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

Лучший способ передать команды функции запуска — использовать список, где lst[0] это команда для вызова (ls в данном случае) и lst[n] являются аргументами этой команды.

Если мы это сделаем, наш код будет выглядеть так.

import subprocess

# subprocess.run('ls')  # Simple command

# subprocess.run('ls -la', shell=True) # Dangerous command

subprocess.run(['ls', '-la'])

Если мы хотим сохранить стандартный вывод подпроцесса в переменной, мы можем сделать это, установив для аргумента capture_output значение true.

list_of_files = subprocess.run(['ls', '-la'], capture_output=True)

print(list_of_files.stdout)

❯ python list_dir.py 
b'total 36ndrwxr-xr-x 3 daniel daniel 4096 may 20 21:08 .ndrwx------ 30 daniel daniel 4096 may 20 18:03 ..n-rw-r--r-- 1 daniel daniel 55 may 20 20:18 example.pyndrwxr-xr-x 8 daniel daniel 4096 may 20 17:31 .gitn-rw-r--r-- 1 daniel daniel 2160 may 17 22:23 .gitignoren-rw-r--r-- 1 daniel daniel 271 may 20 19:53 internet_checker.pyn-rw-r--r-- 1 daniel daniel 1076 may 17 22:23 LICENSEn-rw-r--r-- 1 daniel daniel 216 may 20 22:12 list_dir.pyn-rw-r--r-- 1 daniel daniel 22 may 17 22:23 README.mdn'

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

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

list_of_files = subprocess.run(['ls', '-la'], capture_output=True, text=True)

print(list_of_files.stdout)

❯ python list_dir.py
total 36
drwxr-xr-x  3 daniel daniel 4096 may 20 21:08 .
drwx------ 30 daniel daniel 4096 may 20 18:03 ..
-rw-r--r--  1 daniel daniel   55 may 20 20:18 example.py
drwxr-xr-x  8 daniel daniel 4096 may 20 17:31 .git
-rw-r--r--  1 daniel daniel 2160 may 17 22:23 .gitignore
-rw-r--r--  1 daniel daniel  271 may 20 19:53 internet_checker.py
-rw-r--r--  1 daniel daniel 1076 may 17 22:23 LICENSE
-rw-r--r--  1 daniel daniel  227 may 20 22:14 list_dir.py
-rw-r--r--  1 daniel daniel   22 may 17 22:23 README.md

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

Примеры использования подпроцессов в Python

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

Проверить программу

Одно из основных применений этой библиотеки — разрешить выполнение простых операций ОС.

Например, простой скрипт, проверяющий, установлена ​​ли программа. В Linux мы можем сделать это с помощью команды vhich.

'''Program checker with subprocess'''

import subprocess

program = 'git'

process = subprocess. run(['which', program], capture_output=True, text=True)

if process.returncode == 0: 
    print(f'The program "{program}" is installed')

    print(f'The location of the binary is: {process.stdout}')
else:
    print(f'Sorry the {program} is not installed')

    print(process.stderr)

Примечание. В UNIX при успешном выполнении команды ее код состояния равен 0. В противном случае что-то пошло не так во время выполнения.

Поскольку мы не используем аргумент shell=True, мы можем безопасно принимать пользовательский ввод. Мы также можем проверить, является ли вход допустимой программой с шаблоном регулярного выражения.

import subprocess

import re

programs = input('Separe the programs with a space: ').split()

secure_pattern = 'Нед'

for program in programs:

    if not re.match(secure_pattern, program):
        print("Sorry we can't check that program")

        continue

    process = subprocess. run(
        ['which', program], capture_output=True, text=True)

    if process.returncode == 0:
        print(f'The program "{program}" is installed')

        print(f'The location of the binary is: {process.stdout}')
    else:
        print(f'Sorry the {program} is not installed')

        print(process.stderr)

    print('n')

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

Простой Grep в Python

У вашего друга Тома есть список шаблонов в текстовом файле и еще один большой файл, в котором он хочет получить количество совпадений для каждого шаблона. Он часами запускал команду grep для каждого шаблона.

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

import subprocess

patterns_file="patterns.txt"
readfile="romeo-full.txt"

with open(patterns_file, 'r') as f:
    for pattern in f:
        pattern = pattern.strip()

        process = subprocess.run(
            ['grep', '-c', f'{pattern}', readfile], capture_output=True, text=True)

        if int(process.stdout) == 0:
            print(
                f'The pattern "{pattern}" did not match any line of {readfile}')

            continue

        print(f'The pattern "{pattern}" matched {process.stdout.strip()} times')

Глядя на этот файл, мы определяем две переменные, которые являются именами файлов, с которыми мы хотим работать. Затем открываем файл со всеми выкройками и просматриваем их. Затем мы вызываем подпроцесс, который запускает команду grep с флагом «-c» (значение числа) и указываем результат совпадения с помощью условного оператора.

Если вы запустите этот файл (помните, что вы можете скачать текстовые файлы из репозитория Github)

Настройте virtualenv с подпроцессом

Одна из лучших вещей, которые вы можете сделать с Python, — это автоматизировать процессы. Этот тип сценария может сэкономить вам часы времени в неделю.

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

import subprocess

from pathlib import Path


VENV_NAME = '.venv'
REQUIREMENTS = 'requirements.txt'

process1 = subprocess.run(['which', 'python3'], capture_output=True, text=True)

if process1.returncode != 0:
    raise OSError('Sorry python3 is not installed')

python_bin = process1.stdout.strip()

print(f'Python found in: {python_bin}')

process2 = subprocess.run('echo "$SHELL"', shell=True, capture_output=True, text=True)

shell_bin = process2.stdout.split("https://nikitavolkov.ru/")[-1]

create_venv = subprocess.run([python_bin, '-m', 'venv', VENV_NAME], check=True)

if create_venv.returncode == 0:
    print(f'Your venv {VENV_NAME} has been created')

pip_bin = f'{VENV_NAME}/bin/pip3'

if Path(REQUIREMENTS).exists():
    print(f'Requirements file "{REQUIREMENTS}" found')
    print('Installing requirements')
    subprocess.run([pip_bin, 'install', '-r', REQUIREMENTS])

    print('Process completed! Now activate your environment with "source .venv/bin/activate"')

else:
    print("No requirements specified ...")

В этом случае мы используем несколько процессов и анализируем нужные нам данные в нашем скрипте Python. Мы также используем библиотеку pathlib, которая позволяет нам выяснить, существует ли файл requirements.tkt.

Если вы запустите файл Python, вы получите несколько полезных сообщений о том, что происходит с ОС.

❯ python setup.py 
Python found in: /usr/bin/python3
Your venv .venv has been created
Requirements file "requirements.txt" found
Installing requirements
Collecting asgiref==3.3.4 .......
Process completed! Now activate your environment with "source .venv/bin/activate"

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

Начать другой язык программирования

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

Например, давайте создадим программу hello world на C++ и Java. Чтобы выполнить следующий файл, вам потребуется установить компиляторы C++ и Java.

привет мир.cpp

#include 

int main(){
    std::cout 

хелловорлд.јава

class HelloWorld{  
    public static void main(String args[]){  
     System.out.println("This is a hello world in Java");  
    }  
}  

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

Мы создадим скрипт Python, который запускает все файлы C++ и Java в каталоге. Для этого мы сначала хотим получить список файлов в зависимости от расширения файла, и glob позволяет нам сделать это легко!

from glob import glob

# Gets files with each extension
java_files = glob('*.java')

cpp_files = glob('*.cpp')

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

for file in cpp_files:
    process = subprocess.run(f'g++ {file} -o out; ./out', shell=True, capture_output=True, text=True)
    
    output = process.stdout.strip() + ' BTW this was runned by Python'

    print(output)

for file in java_files:
    without_ext = file.strip('.java')
    process = subprocess.run(f'java {file}; java {without_ext}',shell=True, capture_output=True, text=True)

    output = process.stdout.strip() + ' A Python subprocess runned this :)'
    print(output)

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

Примечание. Будьте осторожны при запуске больших файлов Java или C++, поскольку мы загружаем их выходные данные в память, а это может привести к утечке памяти.

Открытые внешние программы

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

Давайте попробуем, открыв замки, мой любимый веб-браузер.

import subprocess

subprocess.run('brave')

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

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

import subprocess

subprocess.run(['brave', '--incognito'])

Подводить итоги

Подпроцесс — это компьютерный процесс, созданный другим процессом. Мы можем проверить процессы, запущенные на нашем компьютере, с помощью таких инструментов, как htop и диспетчер задач.

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

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

Наконец, помните, что лучший способ научиться — это создать то, что вы хотели бы использовать.

Поделиться в соцсетях