Вопрос-Ответ

How do I execute a program or call a system command?

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

Как мне вызвать внешнюю команду в Python, как если бы я ввел ее в командной строке?

Переведено автоматически
Ответ 1

Использование subprocess.run:

import subprocess

subprocess.run(["ls", "-l"])

Другой распространенный способ - это os.system но вы не должны его использовать, потому что это небезопасно, если какие-либо части команды поступают извне вашей программы или могут содержать пробелы или другие специальные символы, также subprocess.run обычно более гибкий (вы можете получить stdout, stderr "реальный" код состояния, лучшую обработку ошибок и т.д.). Даже документация для os.system рекомендует использовать subprocess вместо этого.

В Python 3.4 и более ранних версиях используйте subprocess.call вместо .run:

subprocess.call(["ls", "-l"])
Ответ 2

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


  1. os.system передает команду и аргументы в командную оболочку вашей системы. Это удобно, потому что таким образом вы действительно можете запускать несколько команд одновременно и настраивать каналы и перенаправление ввода-вывода. Например:


    os.system("some_command < input_file | another_command > output_file")  

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



  2. os.popen будет делать то же самое, что и os.system за исключением того, что он предоставляет вам файлоподобный объект, который вы можете использовать для доступа к стандартному вводу / выводу для этого процесса. Есть 3 других варианта popen, которые все обрабатывают ввод-вывод немного по-другому. Если вы передаете все в виде строки, то ваша команда передается командной оболочке; если вы передаете их в виде списка, то вам не нужно беспокоиться о том, что что-либо экранируется. Пример:


    print(os.popen("ls -l").read())


  3. subprocess.Popen. Это предназначено для замены os.popen, но имеет недостаток в том, что оно немного сложнее из-за своей полноты. Например, вы бы сказали:


    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()

    вместо


    print os.popen("echo Hello World").read()

    но приятно иметь все опции в одном унифицированном классе вместо 4 разных всплывающих функций. Смотрите Документацию.



  4. subprocess.call. По сути, это то же самое, что класс Popen и принимает все те же аргументы, но он просто ожидает завершения команды и выдает вам код возврата. Например:


    return_code = subprocess.call("echo Hello World", shell=True)


  5. subprocess.run. Только Python 3.5+. Аналогично приведенному выше, но еще более гибко и возвращает CompletedProcess объект по завершении выполнения команды.



  6. os.fork, os.exec, os.spawn похожи на свои аналоги на языке Си, но я не рекомендую использовать их напрямую.



Вероятно, вы должны использовать subprocess модуль.

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

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

и представьте, что пользователь вводит что-то "my mama didnt love me && rm -rf /", что может стереть всю файловую систему.

Ответ 3

Типичная реализация:

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
print line,
retval = p.wait()

Вы вольны делать что хотите с stdout данными в канале. На самом деле, вы можете просто опустить эти параметры (stdout= и stderr=), и он будет вести себя следующим образом os.system().

Ответ 4

Несколько советов по отделению дочернего процесса от вызывающего (запуск дочернего процесса в фоновом режиме).

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

Классический пример из документации модуля подпроцесса:

import subprocess
import sys

# Some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # Call subprocess

# Some more code here

Идея здесь в том, что вы не хотите ждать в строке "вызвать подпроцесс", пока не будет завершено выполнение longtask.py. Но не ясно, что происходит после строки "еще немного кода здесь" из примера.

Моей целевой платформой была FreeBSD, но разработка велась под Windows, поэтому сначала я столкнулся с проблемой в Windows.

ОС Windows (Windows ХР), родительский процесс не завершится до тех пор, пока longtask.py закончил свою работу. Это не то, что вы хотите в сценарий CGI. Проблема не специфична для Python; в сообществе PHP проблемы те же.

Решение состоит в том, чтобы передать DETACHED_PROCESS Флаг создания процесса базовой функции CreateProcess в Windows API. Если у вас установлен pywin32, вы можете импортировать флаг из модуля win32process, в противном случае вам следует определить его самостоятельно:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
creationflags=DETACHED_PROCESS).pid

/* UPD 2015.10.27 @eryksun в комментарии ниже отмечает, что семантически правильным флагом является CREATE_NEW_CONSOLE (0x00000010) */

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

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

Я не проверял код на других платформах и не знаю причин поведения во FreeBSD. Если кто-нибудь знает, пожалуйста, поделитесь своими идеями. Поиск в Google по запуску фоновых процессов в Python пока не проливает никакого света.

python subprocess