Subprocess.run: примеры (PYTHON)

Руководство по работе с subprocess.run для выполнения внешних команд в Python
Раздел: Подпроцессы, Управление процессами
subprocess.run(args, capture_output, shell, timeout, check, ...): subprocess.CompletedProcess

Функция subprocess.run в Python

Функция subprocess.run представляет собой основной интерфейс модуля subprocess для выполнения внешних команд. Она применяется в ситуациях, когда требуется запустить отдельный процесс, дождаться его завершения и получить результат выполнения. Этот подход заменяет ряд устаревших функций, таких как os.system и os.spawn*.

Аргументы функции

  • args: обязательный аргумент, принимающий строку или последовательность строк (список, кортеж). Если передается строка, интерпретация зависит от shell. При передаче списка первый элемент определяет команду, последующие - её аргументы.
  • capture_output: при значении True происходит перехват stdout и stderr. Альтернатива ручному указанию subprocess.PIPE для stdout и stderr. Появился в Python 3.7.
  • cwd: строка, указывающая рабочую директорию для выполнения команды.
  • env: словарь с переменными окружения. Если None, используется окружение текущего процесса.
  • check: если установлено True, при ненулевом коде возврата вызывается исключение CalledProcessError.
  • shell: при True команда выполняется через системный shell (например, /bin/sh). Позволяет использовать shell-специфичные конструкции, но требует осторожности из-за рисков безопасности.
  • text (ранее universal_newlines): при True потоки ввода/вывода открываются в текстовом режиме с кодировкой по умолчанию.
  • encoding: кодировка для текстовых потоков.
  • errors: обработка ошибок кодировки/декодировки.
  • timeout: время в секундах, по истечении которого процесс будет прерван с генерацией TimeoutExpired.
  • stdin, stdout, stderr: определяют стандартные потоки процесса. Возможные значения: subprocess.PIPE, subprocess.DEVNULL, существующий файловый дескриптор или открытый файловый объект.

Возвращаемое значение

Функция возвращает объект CompletedProcess, содержащий следующие атрибуты:

  • args: аргументы, переданные для выполнения.
  • returncode: код возврата процесса. Нулевое значение обычно означает успешное выполнение.
  • stdout: перехваченный стандартный вывод (байты или строка в зависимости от text).
  • stderr: перехваченный стандартный вывод ошибок.

Примеры использования subprocess.run

Простой вызов команды

import subprocess
result = subprocess.run(["echo", "Hello, World!"])
print(f"Return code: {result.returncode}")
Return code: 0

Захват вывода

import subprocess
result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
print("Stdout:")
print(result.stdout)
print(f"Stderr: {result.stderr}")
Stdout:
total 0
-rw-r--r-- 1 user group 0 Jan 1 00:00 file.txt
Stderr:

Проверка кода возврата с исключением

import subprocess
try:
    result = subprocess.run(["false"], check=True)
except subprocess.CalledProcessError as e:
    print(f"Command failed with code {e.returncode}")
Command failed with code 1

Использование shell

import subprocess
result = subprocess.run("echo $HOME", shell=True, capture_output=True, text=True)
print(f"Home directory: {result.stdout.strip()}")
Home directory: /home/user

Установка таймаута

import subprocess
try:
    result = subprocess.run(["sleep", "5"], timeout=2)
except subprocess.TimeoutExpired:
    print("Process timed out")
Process timed out

Похожие функции в Python

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

  • subprocess.Popen: базовый класс для создания и управления процессами. Предоставляет большую гибкость, но требует ручного управления. Используется, когда необходим асинхронный запуск или взаимодействие с процессом во время его выполнения.
  • subprocess.call и subprocess.check_call: устаревшие функции, возвращающие только код возврата. check_call генерирует исключение при ненулевом коде.
  • subprocess.check_output: возвращает вывод команды, генерируя исключение при ошибке. Удобна для сценариев, где требуется только результат выполнения.
  • subprocess.getstatusoutput: возвращает кортеж (код возврата, вывод). Выполняется через shell.

subprocess.run рекомендуется как универсальная замена для большинства сценариев, объединяющая возможности предыдущих функций.

Аналоги функции в других языках

PHP

// exec - возвращает последнюю строку вывода
$output = exec('ls -l', $fullOutput, $returnCode);
echo "Output: $output\n";
print_r($fullOutput);
Output: total 0
Array
(
    [0] => total 0
    [1] => -rw-r--r-- 1 user group 0 Jan 1 00:00 file.txt
)

JavaScript (Node.js)

const { execSync } = require('child_process');
const output = execSync('echo Hello', { encoding: 'utf8' });
console.log(output);
Hello

Java

import java.io.IOException;

Process process = Runtime.getRuntime().exec("echo Hello");
process.waitFor();
int exitCode = process.exitValue();
System.out.println("Exit code: " + exitCode);
Exit code: 0

C#

using System.Diagnostics;

ProcessStartInfo startInfo = new ProcessStartInfo("echo", "Hello");
startInfo.RedirectStandardOutput = true;
Process process = Process.Start(startInfo);
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
Console.WriteLine($"Output: {output}");
Output: Hello

Golang

package main

import (
    "fmt"
    "os/exec"
)

func main() {
    out, err := exec.Command("echo", "Hello").Output()
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(string(out))
}
Hello

Kotlin

import java.lang.ProcessBuilder

val process = ProcessBuilder("echo", "Hello").start()
val output = process.inputStream.bufferedReader().readText()
process.waitFor()
println("Output: $output")
Output: Hello

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

Типичные ошибки при использовании

Использование строки с аргументами без shell=True

import subprocess
# Ошибка: команда с аргументами как одна строка
result = subprocess.run("ls -l")
FileNotFoundError: [Errno 2] No such file or directory: 'ls -l'

Неверная обработка вывода

import subprocess
result = subprocess.run(["echo", "test"], capture_output=True)
print(result.stdout)  # вывод в байтах без декодирования
b'test\n'

Бесконечное ожидание при чтении из pipe

import subprocess
# Дочерний процесс ждет ввода, а родительский - вывода
result = subprocess.run(["cat"], input="", capture_output=True, text=True)
# Процесс зависает, ожидая ввода

Использование shell=True с ненадежными данными

import subprocess
user_input = "; rm -rf /"  # опасная команда
subprocess.run(f"echo {user_input}", shell=True)

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

Изменения в последних версиях Python

  • Python 3.5: появилась функция subprocess.run, заменившая несколько старых функций.
  • Python 3.6: добавлен параметр encoding и errors.
  • Python 3.7: введен параметр capture_output как упрощение для capture stdout и stderr. Добавлен текст в исключение TimeoutExpired.
  • Python 3.8: в Windows улучшена обработка Ctrl+C.
  • Python 3.9: параметр text стал предпочтительным синонимом для universal_newlines.
  • Python 3.10: добавлена возможность передачи os.PathLike объектов в cwd.
  • Python 3.11: улучшена производительность на Windows.

Расширенные примеры использования

Передача данных в стандартный ввод

Пример python
import subprocess
# Передача строки в stdin процесса
result = subprocess.run(
    ["grep", "python"],
    input="hello\npython world\ngoodbye\n",
    capture_output=True,
    text=True
)
print(f"Found: {result.stdout}")
Found: python world

Использование переменных окружения

Пример python
import subprocess
import os

custom_env = os.environ.copy()
custom_env["MY_VAR"] = "custom_value"

result = subprocess.run(
    ["printenv", "MY_VAR"],
    env=custom_env,
    capture_output=True,
    text=True
)
print(f"Variable value: {result.stdout.strip()}")
Variable value: custom_value

Параллельный запуск процессов

Пример python
import subprocess
from concurrent.futures import ThreadPoolExecutor

def run_command(cmd):
    return subprocess.run(cmd, capture_output=True, text=True)

commands = [
    ["sleep", "1"],
    ["echo", "hello"],
    ["ls", "-la"]
]

with ThreadPoolExecutor() as executor:
    results = list(executor.map(run_command, commands))
    for res in results:
        print(f"Command completed with code {res.returncode}")
Command completed with code 0
Command completed with code 0
Command completed with code 0

Обработка stderr отдельно от stdout

Пример python
import subprocess
import sys

result = subprocess.run(
    ["ls", "nonexistent_file", "-la"],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True
)

if result.returncode != 0:
    print(f"Error occurred: {result.stderr}", file=sys.stderr)
else:
    print(result.stdout)
Error occurred: ls: cannot access 'nonexistent_file': No such file or directory

Использование cwd для смены рабочей директории

Пример python
import subprocess
import tempfile
import os

with tempfile.TemporaryDirectory() as tmpdir:
    # Создаем файл во временной директории
    test_file = os.path.join(tmpdir, "test.txt")
    with open(test_file, "w") as f:
        f.write("content")
    
    # Запускаем ls во временной директории
    result = subprocess.run(
        ["ls", "-l"],
        cwd=tmpdir,
        capture_output=True,
        text=True
    )
    print(f"Files in temp directory:\n{result.stdout}")
Files in temp directory:
total 4
-rw-r--r-- 1 user group 7 Jan 1 00:00 test.txt

Комбинирование stderr с stdout

Пример python
import subprocess

# Перенаправляем stderr в stdout
result = subprocess.run(
    ["ls", "existing_file", "nonexistent_file"],
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,  # особое значение
    text=True
)
print(f"Combined output:\n{result.stdout}")
Combined output:
existing_file
ls: cannot access 'nonexistent_file': No such file or directory

питон subprocess.run function comments

En
Subprocess.run Run command with arguments