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

Функция subprocess.call в Python: практические примеры применения
Раздел: Подпроцессы, Управление процессами
subprocess.call(args, stdin, stdout, stderr, shell, timeout): int

Основы функции subprocess.call

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

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

  • args: обязательный аргумент. Может быть строкой или последовательностью (список, кортеж). Для строки интерпретация зависит от аргумента shell. Для последовательности первый элемент - это исполняемая команда, остальные - её аргументы.
  • stdin: стандартный ввод для дочернего процесса. По умолчанию None, что означает перенаправление из файла /dev/null. Может быть файловым объектом, файловым дескриптором или subprocess.PIPE.
  • stdout: стандартный вывод. Аналогично stdin, по умолчанию None (вывод направляется в консоль).
  • stderr: стандартный вывод ошибок. По умолчанию None (ошибки направляются в консоль).
  • shell: логическое значение. Если True, команда выполняется через оболочку системы (обычно /bin/sh в Unix). Это позволяет использовать возможности оболочки, такие как подстановка файлов или переменные среды, но может быть источником уязвимостей при использовании пользовательского ввода.
  • cwd: строка, указывающая текущий рабочий каталог для выполнения команды.
  • timeout: время в секундах, по истечении которого функция завершится с исключением TimeoutExpired. Доступно в Python 3.3+.
  • env: словарь с переменными среды для дочернего процесса. Если None, используется окружение текущего процесса.

Возвращаемое значение: целое число - код завершения дочернего процесса. Обычно 0 означает успешное выполнение, другие значения указывают на ошибку (код зависит от конкретной команды). Если процесс завершился по сигналу, возвращается отрицательный номер сигнала.

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

Пример 1: Простой вызов команды ls (Unix/Linux) или dir (Windows).

import subprocess

# Для Unix/Linux
return_code = subprocess.call([\"ls\", \"-l\"])
print(\"Код возврата:\", return_code)
Код возврата: 0

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

import subprocess

# Через оболочку
return_code = subprocess.call(\"echo Hello, World!\", shell=True)
print(\"Код возврата:\", return_code)
Hello, World!
Код возврата: 0

Пример 3: Задание рабочего каталога и обработка таймаута.

import subprocess
import time

try:
    # Команда sleep на 10 секунд, но таймаут 2 секунды
    return_code = subprocess.call([\"sleep\", \"10\"], timeout=2)
except subprocess.TimeoutExpired:
    print(\"Процесс не завершился за отведенное время\")
Процесс не завершился за отведенное время

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

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

  • subprocess.run(): рекомендованная функция в Python 3.5+. Возвращает объект CompletedProcess, содержащий код возврата, stdout и stderr. Более гибкая и функциональная, чем call.
  • subprocess.check_call(): аналогична call, но генерирует исключение CalledProcessError, если код возврата не нулевой.
  • subprocess.check_output(): возвращает вывод команды (stdout) в виде байтовой строки. Также генерирует исключение при ненулевом коде возврата.
  • subprocess.Popen(): низкоуровневый интерфейс для создания процессов. Предоставляет максимальный контроль: неблокирующее выполнение, передача данных через каналы, ожидание завершения отдельным вызовом. Используется, когда требуется сложное взаимодействие с процессом.

subprocess.call() предпочтительнее для простых задач, где нужен только код возврата, и не требуется захват вывода. Для большинства новых проектов рекомендуется использовать subprocess.run() из-за её больших возможностей и понятного API.

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

Разные языки предоставляют свои механизмы для запуска внешних процессов.

PHP: функция exec() или shell_exec().

<?php
$output = exec(\"ls -l\", $output_array, $return_code);
echo \"Код возврата: \" . $return_code . \"\\n\";
?>
Код возврата: 0

JavaScript (Node.js): модуль child_process, функция exec() или spawn().

const { exec } = require(\"child_process\");
exec(\"ls -l\", (error, stdout, stderr) => {
    console.log(`Код возврата: ${error ? error.code : 0}`);
});
Код возврата: 0

Java: класс ProcessBuilder или Runtime.exec().

import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        Process process = new ProcessBuilder(\"ls\", \"-l\").start();
        int exitCode = process.waitFor();
        System.out.println(\"Код возврата: \" + exitCode);
    }
}
Код возврата: 0

Go: пакет os/exec, метод Run().

package main

import (
    \"fmt\"
    \"os/exec\"
)

func main() {
    cmd := exec.Command(\"ls\", \"-l\")
    err := cmd.Run()
    if err != nil {
        fmt.Println(\"Код возврата (ошибка):\", err)
    } else {
        fmt.Println(\"Код возврата: 0\")
    }
}
Код возврата: 0

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

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

Ошибка 1: Неправильный путь к исполняемому файлу или команда не найдена.

import subprocess

try:
    subprocess.call([\"non_existent_command\"])
except FileNotFoundError as e:
    print(\"Ошибка:\", e)
Ошибка: [Errno 2] No such file or directory: 'non_existent_command'

Ошибка 2: Использование строки с аргументами без shell=True, когда команда содержит пробелы или специальные символы.

import subprocess

# Неправильно: строка без shell=True разбивается неправильно
subprocess.call(\"ls -l\")  # Ищет команду с именем 'ls -l' (с пробелом)
FileNotFoundError: [Errno 2] No such file or directory: 'ls -l'

Правильный подход: либо использовать список, либо установить shell=True.

Ошибка 3: Проблемы с безопасностью при использовании shell=True с пользовательским вводом.

import subprocess

user_input = \"; rm -rf /\"  # Опасная команда
# Никогда так не делать!
# subprocess.call(f\"echo {user_input}\", shell=True)

Во избежание инъекций команд рекомендуется использовать списки аргументов и избегать shell=True, когда возможен пользовательский ввод.

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

В Python 3.5 была введена функция subprocess.run(), которая стала рекомендуемой заменой для call, check_call и check_output. Однако subprocess.call() остаётся доступной для обратной совместимости.

В Python 3.3 был добавлен параметр timeout для call, check_call и check_output. При превышении таймаута генерируется исключение TimeoutExpired.

Начиная с Python 3.8, в subprocess.run() (и другие функции) добавлен параметр capture_output для удобства захвата stdout и stderr. Однако subprocess.call() не получила этого параметра, так как не предназначена для захвата вывода.

В Python 3.12 были оптимизированы некоторые внутренние аспекты работы модуля subprocess, но публичный API call не претерпел значительных изменений.

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

Пример 1: Запуск команды с перенаправлением стандартного вывода в файл.

import subprocess

with open(\"output.txt\", \"w\") as f:
    return_code = subprocess.call([\"ls\", \"-l\"], stdout=f)

print(\"Код возврата:\", return_code)
# Содержимое файла output.txt будет содержать вывод ls -l
Код возврата: 0

Пример 2: Использование переменных среды для дочернего процесса.

import subprocess
import os

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

# Передаем собственное окружение
return_code = subprocess.call([\"printenv\", \"MY_VAR\"], env=custom_env)
print(\"Код возврата:\", return_code)
Hello
Код возврата: 0

Пример 3: Выполнение команды с разными кодами возврата и их интерпретация.

import subprocess

# Команда test возвращает 0, если условие истинно, иначе 1
return_code = subprocess.call([\"test\", \"1\", \"-eq\", \"1\"])
print(\"Код возврата при истинном условии:\", return_code)

return_code = subprocess.call([\"test\", \"1\", \"-eq\", \"2\"])
print(\"Код возврата при ложном условии:\", return_code)
Код возврата при истинном условии: 0
Код возврата при ложном условии: 1

Пример 4: Комбинирование stderr и stdout с использованием subprocess.STDOUT.

import subprocess

# stderr перенаправляется в stdout
return_code = subprocess.call(
    [\"ls\", \"-l\", \"non_existent_file\"],
    stderr=subprocess.STDOUT
)
print(\"Код возврата:\", return_code)
ls: cannot access 'non_existent_file': No such file or directory
Код возврата: 2

Пример 5: Выполнение команды в оболочке с передачей сложной строки.

import subprocess

# Использование возможностей оболочки: подстановка команд и перенаправление
cmd = \"echo Current user: $(whoami) > user_info.txt\"
return_code = subprocess.call(cmd, shell=True)
print(\"Код возврата:\", return_code)
# В файле user_info.txt будет строка \"Current user: username\"
Код возврата: 0

питон subprocess.call function comments

En
Subprocess.call Run command and wait to complete