Как мне выполнить программу или вызвать системную команду?
Как мне вызвать внешнюю команду в 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
Вот краткое описание способов вызова внешних программ, включая их преимущества и недостатки:
os.system
передает команду и аргументы в командную оболочку вашей системы. Это удобно, потому что таким образом вы действительно можете запускать несколько команд одновременно и настраивать каналы и перенаправление ввода-вывода. Например:os.system("some_command < input_file | another_command > output_file")
Однако, хотя это удобно, вам придется вручную обрабатывать экранирование символов оболочки, таких как пробелы и так далее. С другой стороны, это также позволяет запускать команды, которые являются просто командами оболочки, а не внешними программами.
os.popen
будет делать то же самое, что иos.system
за исключением того, что он предоставляет вам файлоподобный объект, который вы можете использовать для доступа к стандартному вводу / выводу для этого процесса. Есть 3 других варианта popen, которые все обрабатывают ввод-вывод немного по-другому. Если вы передаете все в виде строки, то ваша команда передается командной оболочке; если вы передаете их в виде списка, то вам не нужно беспокоиться о том, что что-либо экранируется. Пример:print(os.popen("ls -l").read())
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 разных всплывающих функций. Смотрите Документацию.
subprocess.call
. По сути, это то же самое, что классPopen
и принимает все те же аргументы, но он просто ожидает завершения команды и выдает вам код возврата. Например:return_code = subprocess.call("echo Hello World", shell=True)
subprocess.run
. Только Python 3.5+. Аналогично приведенному выше, но еще более гибко и возвращаетCompletedProcess
объект по завершении выполнения команды.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 пока не проливает никакого света.