Как вызвать внешнюю команду (как если бы я набрал ее в оболочке Unix или в командной строке Windows) из сценария Python?






import os
os.system("your command")
Обратите внимание, что это опасно, поскольку команда не очищается. Я оставляю на ваше усмотрение поиск в Google соответствующей документации по модулям 'os' и 'sys'. Есть несколько функций (exec * и spawn *), которые будут делать похожие вещи.
Теперь это должно указывать на subprocess как на более универсальное и портативное решение. Выполнение внешних команд, конечно, по своей сути непереносимо (вы должны убедиться, что команда доступна для каждой архитектуры, которую вам нужно поддерживать), а передача пользовательского ввода в качестве внешней команды по своей сути небезопасна.
Обратите внимание на временную метку этого парня: «правильный» ответ имеет 40-кратное количество голосов и является ответом №1.
Единственное решение, которое сработало для меня для работы с NodeJS.
import os
cmd = 'ls -al'
os.system(cmd)
Если вы хотите вернуть результаты команды, вы можете использовать os.popen. Однако это устарело с версии 2.6 в пользу модуль подпроцесса, что хорошо покрыто другими ответами.
popen не рекомендуется в пользу подпроцесс.
Вы также можете сохранить свой результат с помощью вызова os.system, поскольку он работает как сама оболочка UNIX, например, os.system ('ls -l> test2.txt')
Используйте модуль subprocess в стандартной библиотеке:
import subprocess
subprocess.run(["ls", "-l"])
Преимущество subprocess.run перед os.system в том, что он более гибкий (вы можете получить stdout, stderr, "настоящий" код статуса, лучше обработка ошибок и т. д.).
Даже документация для os.system рекомендует вместо этого использовать subprocess:
The
subprocessmodule provides more powerful facilities for spawning new processes and retrieving their results; using that module is preferable to using this function. See the Replacing Older Functions with the subprocess Module section in thesubprocessdocumentation for some helpful recipes.
В Python 3.4 и более ранних версиях используйте subprocess.call вместо .run:
subprocess.call(["ls", "-l"])
Есть ли способ использовать подстановку переменных? IE Я попытался сделать echo $PATH с помощью call(["echo", "$PATH"]), но он просто повторил буквальную строку $PATH вместо выполнения какой-либо замены. Я знаю, что могу получить переменную среды PATH, но мне интересно, есть ли простой способ заставить команду вести себя точно так, как если бы я выполнил ее в bash.
@KevinWheeler Для этого вам нужно использовать shell=True.
@KevinWheeler Вы НЕ должны использовать shell=True, для этой цели Python поставляется с os.path.expandvars. В вашем случае вы можете написать: os.path.expandvars("$PATH"). @SethMMorton, пожалуйста, пересмотрите свой комментарий -> Почему бы не использовать shell = True
блокирует вызов? то есть, если я хочу запустить несколько команд в цикле for, как мне это сделать, чтобы он не блокировал мой скрипт python? Меня не волнует вывод команды, я просто хочу запустить их много.
Если вы хотите создать список из команды с параметрами, список, который можно использовать с subprocess, когда shell=False, тогда используйте shlex.split для простого способа сделать это docs.python.org/2/library/shlex.html#shlex.split
subprocess также позволяет напрямую связывать две команды вместе, пример есть в документации.
@CharlieParker: блоки call, check_call, check_output и run. Если вы хотите неблокировать, используйте subprocess.Popen.
Вы также можете передать строку вместо списка строк: subprocess.run("ls -l")
Если они хотят назвать его run вместо call, почему бы просто не сделать его псевдонимом? Некоторые типы обратной совместимости трудны и позволяют продолжить работу подверженных ошибкам шаблонов, но создание run == call легко, просто и почти не требует затрат. Почему call для версий Python до 3.5 вместо новых и старых версий?
@SamuelMuldoon Проблема в том, что subprocess.call имеет совершенно другую семантику, чем subprocess.run. subprocess.call все еще существует в Python 3.5–3.8 (для обратной совместимости), просто, если он у вас есть, subprocess.run безусловно лучше.
Я пытаюсь использовать эту команду для запуска команд excalibur с python для подключения к локальному хосту, но это не работает, я получаю FileNotFoundError. import subprocess subprocess.run(["excalibur initdb", "excalibur webserver"])
@ChachoFuva список, который вы передаете subprocess.run, представляет собой список слов, представляющих 1 команду, а не список команд. Это должно быть subprocess.run(["excalibur", "initdb"]), затем subprocess.run(["excalibur", "webserver"])
Любое значение в создании нового потока с помощью subprocess.run () и при условии, что исходный поток убивает его, если он превышает некоторый тайм-аут?
Я бы рекомендовал использовать модуль подпроцесс вместо os.system, потому что он выполняет экранирование оболочки за вас и, следовательно, намного безопаснее.
subprocess.call(['ping', 'localhost'])
Если вы хотите создать список из команды с параметрами, список, который можно использовать с subprocess, когда shell=False, тогда используйте shlex.split для простого способа сделать это docs.python.org/2/library/shlex.html#shlex.split (это рекомендуемый способ в соответствии с документами docs.python.org/2/library/subprocess.html#popen-constructor)
Это неверно: «он убегает за вас и поэтому намного безопаснее». subprocess не выполняет экранирование оболочки, подпроцесс не передает вашу команду через оболочку, поэтому нет необходимости в экранировании оболочки.
os.system в порядке, но немного устарел. Это тоже не очень безопасно. Вместо этого попробуйте subprocess. subprocess не вызывает sh напрямую и поэтому более безопасен, чем os.system.
Получить дополнительную информацию здесь.
Хотя я согласен с общей рекомендацией, subprocess не устраняет всех проблем с безопасностью и имеет собственные неприятные проблемы.
Вот краткое изложение способов вызова внешних программ, а также преимущества и недостатки каждого из них:
os.system("some_command with args") передает команду и аргументы в оболочку вашей системы. Это хорошо, потому что вы можете одновременно запускать несколько команд таким образом и настраивать каналы и перенаправление ввода / вывода. Например:
os.system("some_command < input_file | another_command > output_file")
Однако, хотя это удобно, вы должны вручную обрабатывать экранирование символов оболочки, таких как пробелы и т. д. С другой стороны, это также позволяет запускать команды, которые являются просто командами оболочки, а не внешними программами. См. документация.
stream = os.popen("some_command with args") будет делать то же самое, что и os.system, за исключением того, что он предоставляет вам объект в виде файла, который вы можете использовать для доступа к стандартному вводу / выводу для этого процесса. Есть еще 3 варианта popen, которые обрабатывают ввод-вывод немного по-разному. Если вы передадите все как строку, ваша команда будет передана оболочке; если вы передадите их в виде списка, вам не нужно беспокоиться о том, чтобы от чего-либо уйти. См. документация.
Класс Popen модуля subprocess. Он предназначен для замены os.popen, но имеет недостаток в том, что он немного сложнее из-за того, что он настолько всеобъемлющий. Например, вы бы сказали:
print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
вместо:
print os.popen("echo Hello World").read()
но приятно иметь все параметры в одном унифицированном классе вместо 4 разных функций popen. См. документация.
Функция call из модуля subprocess. По сути, он похож на класс Popen и принимает все те же аргументы, но просто ждет, пока команда завершится, и выдаст вам код возврата. Например:
return_code = subprocess.call("echo Hello World", shell=True)
См. документация.
Если вы используете Python 3.5 или новее, вы можете использовать новую функцию subprocess.run, которая очень похожа на приведенную выше, но даже более гибкая и возвращает объект CompletedProcess, когда команда завершает выполнение.
Модуль os также имеет все функции fork / exec / spawn, которые есть в программе на C, но я не рекомендую использовать их напрямую.
Модуль subprocess, вероятно, должен быть тем, что вы используете.
Наконец, имейте в виду, что для всех методов, в которых вы передаете последнюю команду, которая должна выполняться оболочкой в виде строки, вы несете ответственность за ее экранирование. Есть серьезные проблемы с безопасностью, если какой-либо части передаваемой вами строки нельзя полностью доверять. Например, если пользователь вводит некоторую / любую часть строки. Если вы не уверены, используйте эти методы только с константами. Чтобы дать вам представление о последствиях, рассмотрим этот код:
print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()
и представьте, что пользователь вводит что-то «моя мама не любила меня && rm -rf /», что может стереть всю файловую систему.
Хороший ответ / объяснение. Как этот ответ оправдывает девиз Python, описанный в этой статье? fastcompany.com/3026446/… «Стилистически Perl и Python имеют разные философии. Самый известный девиз Perl -« Есть более чем один способ сделать это ». Python разработан таким образом, чтобы иметь один очевидный способ сделать это« Кажется, должно быть и наоборот! В Perl я знаю только два способа выполнить команду - с помощью обратного тика или open.
Если вы используете Python 3.5+, используйте subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run
Обычно нужно знать, что делается с STDOUT и STDERR дочернего процесса, потому что, если они игнорируются при некоторых (довольно распространенных) условиях, в конечном итоге дочерний процесс выдаст системный вызов для записи в STDOUT (STDERR тоже?) это превысит выходной буфер, предоставленный для процесса ОС, и ОС заставит его блокироваться до тех пор, пока какой-либо процесс не прочитает из этого буфера. Итак, с учетом рекомендуемых в настоящее время способов, subprocess.run(..), что именно означает «По умолчанию это не захватывает stdout или stderr».? А как насчет subprocess.check_output(..) и STDERR?
какая из команд, которые вы рекомендовали, блокирует мой скрипт? то есть, если я хочу запустить несколько команд в цикле for, как мне это сделать, чтобы он не блокировал мой скрипт python? Меня не волнует вывод команды, я просто хочу запустить их много.
@phoenix Я не согласен. Ничто не мешает вам использовать os.system в python3 docs.python.org/3/library/os.html#os.system
Возможно, это неправильный путь. Большинству людей нужен только subprocess.run() или его старшие братья и сестры subprocess.check_call() et al. В случаях, когда этого недостаточно, см. subprocess.Popen(). os.popen(), возможно, не следует упоминать вообще или даже после того, как «взломайте свой собственный код fork / exec / spawn».
Типовая реализация:
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().
.readlines() читает строки все сразу, то есть блокируется до тех пор, пока подпроцесс не завершится (закрывает свой конец канала). Чтобы читать в реальном времени (если нет проблем с буферизацией), вы можете: for line in iter(p.stdout.readline, ''): print line,
Не могли бы вы пояснить, что вы имеете в виду, говоря «если нет проблем с буферизацией»? Если процесс определенно блокируется, вызов подпроцесса также блокируется. То же самое могло произойти и с моим исходным примером. Что еще может случиться с буферизацией?
дочерний процесс может использовать блочную буферизацию в неинтерактивном режиме вместо буферизации строк, поэтому p.stdout.readline() (примечание: нет s в конце) не будет видеть никаких данных, пока дочерний процесс не заполнит свой буфер. Если ребенок не производит много данных, результат не будет в реальном времени. См. Вторую причину в В: Почему бы просто не использовать канал (popen ())?. Предусмотрены некоторые обходные пути в этом ответе (pexpect, pty, stdbuf)
проблема с буферизацией имеет значение только в том случае, если вы хотите выводить данные в реальном времени, и не относится к вашему коду, который ничего не печатает, пока не будут получены данные все
Этот ответ был хорош для своего времени, но мы больше не должны рекомендовать Popen для простых задач. Это также без нужды указывает shell=True. Попробуйте один из ответов subprocess.run().
Здесь есть еще одно отличие, о котором ранее не упоминалось.
subprocess.Popen выполняет <команду> как подпроцесс. В моем случае мне нужно выполнить файл <a>, который должен взаимодействовать с другой программой <b>.
Я попробовал подпроцесс, и выполнение прошло успешно. Однако <b> не смог связаться с <a>. Все нормально, когда оба запускаю с терминала.
Еще один: (ПРИМЕЧАНИЕ: kwrite ведет себя иначе, чем другие приложения. Если вы попробуете описанное ниже с Firefox, результаты будут разными.)
Если вы попробуете os.system("kwrite"), выполнение программы зависнет, пока пользователь не закроет kwrite. Чтобы преодолеть это, я попробовал os.system(konsole -e kwrite). На этот раз программа продолжала работать, но kwrite стал подпроцессом консоли.
Любой запускает kwrite, не являющийся подпроцессом (т.е. в системном мониторе он должен отображаться на крайнем левом краю дерева).
Что вы имеете в виду под "Кто-нибудь запускает kwrite, не являясь подпроцессом"?
Некоторые подсказки по отсоединению дочернего процесса от вызывающего (запуск дочернего процесса в фоновом режиме).
Предположим, вы хотите запустить длинную задачу из сценария CGI. То есть дочерний процесс должен жить дольше, чем процесс выполнения сценария CGI.
Классический пример из документации модуля подпроцесса:
import subprocess
import sys
# Some code here
pid = subprocess.Popen([sys.executable, "longtask.py"]) # Call subprocess
# Some more code here
Идея здесь в том, что вы не хотите ждать в строке «call subprocess», пока longtask.py не завершится. Но непонятно, что происходит после строки «еще немного кода» из примера.
Моей целевой платформой была FreeBSD, но разработка велась в Windows, поэтому сначала я столкнулся с проблемой в Windows.
В Windows (Windows XP) родительский процесс не завершится, пока 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 пока не проливает света.
Я заметил возможную "причуду" при разработке приложений py2exe в pydev + eclipse. я смог сказать, что основной скрипт не был отсоединен, потому что окно вывода eclipse не закрывалось; даже если сценарий выполняется до конца, он все еще ожидает возврата. но когда я попытался скомпилировать исполняемый файл py2exe, происходит ожидаемое поведение (процессы запускаются как отсоединенные, а затем завершаются). Я не уверен, но имени исполняемого файла больше нет в списке процессов. это работает для всех подходов (os.system ("start *"), os.spawnl с os.P_DETACH, subprocs и т. д.)
Попадание в Windows: несмотря на то, что я создал процесс с помощью DETACHED_PROCESS, когда я убил своего демона Python, все порты, открытые им, не освободились, пока все порожденные процессы не завершились. WScript.Shell решил все мои проблемы. Пример здесь: pastebin.com/xGmuvwSx
вам также может понадобиться флаг CREATE_NEW_PROCESS_GROUP. См. Popen, ожидающий дочернего процесса, даже если непосредственный дочерний процесс завершился
Я вижу, что import subprocess as sp;sp.Popen('calc') не ждет завершения подпроцесса. Кажется, что флаги создания не нужны. Что мне не хватает?
@ubershmekel, я не понимаю, что вы имеете в виду, и у меня нет установки Windows. Если я правильно помню, без флагов вы не можете закрыть экземпляр cmd, из которого вы запустили calc.
Я на Windows 8.1 и calc вроде пережил закрытие python.
Имеет ли значение использование «0x00000008»? Это конкретное значение, которое необходимо использовать, или один из нескольких вариантов?
Следующее неверно: «[o] n windows (win xp), родительский процесс не завершится, пока longtask.py не закончит свою работу». Родительский процесс завершится нормально, но окно консоли (экземпляр conhost.exe) закрывается только при выходе из последнего присоединенного процесса, а потомок, возможно, унаследовал родительскую консоль. Установка DETACHED_PROCESS в creationflags позволяет избежать этого, не позволяя дочернему элементу наследовать или создавать консоль. Если вместо этого вам нужна новая консоль, используйте CREATE_NEW_CONSOLE (0x00000010).
Я не имел в виду, что выполнение как отдельного процесса некорректно. Тем не менее, вам может потребоваться установить стандартные дескрипторы для файлов, каналов или os.devnull, потому что в противном случае некоторые консольные программы завершаются с ошибкой. Создайте новую консоль, если вы хотите, чтобы дочерний процесс взаимодействовал с пользователем одновременно с родительским процессом. Было бы непросто попытаться сделать и то, и другое в одном окне.
stdout=subprocess.PIPE заставит ваш код зависнуть, если у вас долгий вывод от ребенка. Подробнее см. thraxil.org/users/anders/posts/2008/03/13/….
нет ли независимого от ОС способа запустить процесс в фоновом режиме?
мне ваш ответ кажется странным. Я только что открыл subprocess.Popen, и ничего плохого не произошло (не пришлось ждать). Почему именно нам нужно беспокоиться о сценарии, который вы указываете? Я настроен скептически.
Также проверьте библиотеку Python "pexpect".
Он позволяет интерактивно управлять внешними программами / командами, даже ssh, ftp, telnet и т. д. Вы можете просто ввести что-то вроде:
child = pexpect.spawn('ftp 192.168.0.24')
child.expect('(?i)name .*: ')
child.sendline('anonymous')
child.expect('(?i)password')
subprocess.check_call удобен, если вы не хотите проверять возвращаемые значения. При любой ошибке генерируется исключение.
Если вам нужен вывод команды, которую вы вызываете, тогда вы можете использовать subprocess.check_output (Python 2.7+).
>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
Также обратите внимание на параметр ракушка.
If shell is
True, the specified command will be executed through the shell. This can be useful if you are using Python primarily for the enhanced control flow it offers over most system shells and still want convenient access to other shell features such as shell pipes, filename wildcards, environment variable expansion, and expansion of ~ to a user’s home directory. However, note that Python itself offers implementations of many shell-like features (in particular,glob,fnmatch,os.walk(),os.path.expandvars(),os.path.expanduser(), andshutil).
Обратите внимание, что check_output требует списка, а не строки. Если вы не полагаетесь на пробелы в кавычках, чтобы сделать ваш вызов действительным, самый простой и читаемый способ сделать это - subprocess.check_output("ls -l /dev/null".split()).
Я всегда использую fabric для таких вещей, как:
from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )
Но это хороший инструмент: sh (интерфейс подпроцесса Python).
Взгляните на пример:
from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)
os.system не позволяет сохранять результаты, поэтому, если вы хотите сохранить результаты в каком-то списке или чем-то еще, subprocess.call подойдет.
Вы можете использовать Popen, а затем вы можете проверить статус процедуры:
from subprocess import Popen
proc = Popen(['ls', '-l'])
if proc.poll() is None:
proc.kill()
Проверьте subprocess.Popen.
Самый простой способ запустить любую команду и вернуть результат:
from commands import getstatusoutput
try:
return getstatusoutput("ls -ltr")
except Exception, e:
return None
Будет ли это устаревшим в Python 3.0?
Действительно, Документация commands из Python 2.7 говорит, что он устарел в версии 2.6 и будет удален в версии 3.0.
Мне очень нравится shell_command за его простоту. Он построен поверх модуля подпроцесса.
Вот пример из документации:
>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py shell_command.py test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan 391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0
Вот как я запускаю свои команды. В этом коде есть все, что вам нужно в значительной степени
from subprocess import Popen, PIPE
cmd = "ls -l ~/"
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
print "Return code: ", p.returncode
print out.rstrip(), err.rstrip()
Я думаю, что это приемлемо для жестко запрограммированных команд, если это увеличивает читаемость.
subprocess.run - это рекомендуемый подход начиная с Python 3.5, если вашему коду не требуется поддерживать совместимость с более ранними версиями Python. Он более последовательный и предлагает ту же простоту использования, что и Envoy. (Однако трубопровод не так прост. См. этот вопрос о том, как.)
Вот несколько примеров из документация.
Запустить процесс:
>>> subprocess.run(["ls", "-l"]) # Doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)
Рейз при неудачном беге:
>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
Вывод захвата:
>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')
Рекомендую попробовать Посланник. Это оболочка для подпроцесса, которая, в свою очередь, стремится заменить для более старых модулей и функций. Посланник - это подпроцесс для людей.
Пример использования из README:
>>> r = envoy.run('git config', data='data to pipe in', timeout=2)
>>> r.status_code
129
>>> r.std_out
'usage: git config [options]'
>>> r.std_err
''
Трубка тоже:
>>> r = envoy.run('uptime | pbcopy')
>>> r.command
'pbcopy'
>>> r.status_code
0
>>> r.history
[<Response 'uptime'>]
Используйте модуль подпроцесса (Python 3):
import subprocess
subprocess.run(['ls', '-l'])
Это рекомендуемый стандартный способ. Однако создание и написание более сложных задач (конвейеры, вывод, ввод и т. д.) Может быть утомительным.
Примечание по версии Python: если вы все еще используете Python 2, subprocess.call работает аналогичным образом.
Подсказка: shlex.split может помочь вам разобрать команду для run, call и других функций subprocess, если вы не хотите (или не можете!) Предоставлять их в виде списков:
import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))
Если вы не против внешних зависимостей, используйте отвес:
from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())
Это лучшая обертка subprocess. Он кроссплатформенный, т.е. работает как в Windows, так и в Unix-подобных системах. Установите pip install plumbum.
Еще одна популярная библиотека - ш:
from sh import ifconfig
print(ifconfig('wlan0'))
Однако sh отказался от поддержки Windows, так что это уже не так здорово, как раньше. Установите pip install sh.
Есть много разных способов запуска внешних команд в Python, и у всех есть свои плюсы и минусы.
Мои коллеги и я пишем инструменты системного администрирования Python, поэтому нам нужно запускать множество внешних команд, и иногда вы хотите, чтобы они блокировались или выполнялись асинхронно, по тайм-ауту, обновлялись каждую секунду и т. д.
Также существуют разные способы обработки кода возврата и ошибок, и вы можете захотеть проанализировать вывод и предоставить новый ввод (в стиле ожидать). Или вам нужно будет перенаправить stdin, stdout и stderr для запуска на другом tty (например, при использовании screen).
Так что вам, вероятно, придется написать много оболочек вокруг внешней команды. Итак, вот модуль Python, который мы написали, который может обрабатывать почти все, что вам нужно, а если нет, он очень гибкий, поэтому вы можете легко его расширить:
https://github.com/hpcugent/vsc-base/blob/master/lib/vsc/utils/run.py
обновление: он не работает автономно и требует некоторых других наших инструментов, и на протяжении многих лет он получил множество специализированных функций, поэтому он может не стать заменой для вас, но может дать вам много информации о том, как внутренности Python для работы с командами и идеи о том, как справляться с определенными ситуациями.
модуль подпроцесса, описанный выше Эли, очень мощный, но синтаксис для выполнения стандартного системного вызова и проверки его вывода излишне многословен.
Самый простой способ сделать системный вызов - использовать модуль команд (только для Linux).
> import commands
> commands.getstatusoutput("grep matter alice-in-wonderland.txt")
(0, "'Then it doesn't matter which way you go,' said the Cat.")
Первый элемент в кортеже - это код возврата процесса. Второй элемент - это стандартный вывод (и стандартная ошибка, объединенная).
Разработчики Python «устарели» модуль команд, но это не значит, что вы не должны его использовать. Только то, что они больше не разрабатывают его, и это нормально, потому что он уже совершенен (в своей маленькой, но важной функции).
Устаревший означает не только «больше не разрабатывается», но и «вам не рекомендуется использовать это». Устаревшие функции могут выйти из строя в любой момент, могут быть удалены в любое время или могут быть опасными. Вы никогда не должны использовать это в важном коде. Устаревание - это просто лучший способ, чем немедленное удаление функции, потому что это дает программистам время адаптироваться и заменить устаревшие функции.
Просто чтобы доказать свою точку зрения: «Не рекомендуется, начиная с версии 2.6: модуль команд был удален в Python 3. Вместо этого используйте модуль подпроцесса».
Это не опасно! Разработчики Python осторожны только в том, чтобы разбивать функции между основными выпусками (то есть между 2.x и 3.x). Я использую модуль команд с Python 2.4 2004 года. То же самое и сегодня работает в Python 2.7.
Под опасным я не имел в виду, что его можно удалить в любое время (это другая проблема), и я не говорил, что использовать этот конкретный модуль опасно. Однако это может стать опасным, если будет обнаружена уязвимость в системе безопасности, но модуль не будет развиваться и не поддерживается. (Я не хочу сказать, что этот модуль уязвим или не уязвим для проблем безопасности, просто говорю об устаревших вещах в целом)
Чтобы добавить к обсуждению, если вы включаете использование консоли Python, вы можете вызывать внешние команды из IPython. Находясь в приглашении IPython, вы можете вызывать команды оболочки с помощью префикса «!». Вы также можете комбинировать код Python с оболочкой и назначать вывод сценариев оболочки переменным Python.
Например:
In [9]: mylist = !ls
In [10]: mylist
Out[10]:
['file1',
'file2',
'file3',]
После некоторого исследования у меня есть следующий код, который мне очень подходит. Он в основном печатает как stdout, так и stderr в реальном времени.
stdout_result = 1
stderr_result = 1
def stdout_thread(pipe):
global stdout_result
while True:
out = pipe.stdout.read(1)
stdout_result = pipe.poll()
if out == '' and stdout_result is not None:
break
if out != '':
sys.stdout.write(out)
sys.stdout.flush()
def stderr_thread(pipe):
global stderr_result
while True:
err = pipe.stderr.read(1)
stderr_result = pipe.poll()
if err == '' and stderr_result is not None:
break
if err != '':
sys.stdout.write(err)
sys.stdout.flush()
def exec_command(command, cwd=None):
if cwd is not None:
print '[' + ' '.join(command) + '] in ' + cwd
else:
print '[' + ' '.join(command) + ']'
p = subprocess.Popen(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd
)
out_thread = threading.Thread(name='stdout_thread', target=stdout_thread, args=(p,))
err_thread = threading.Thread(name='stderr_thread', target=stderr_thread, args=(p,))
err_thread.start()
out_thread.start()
out_thread.join()
err_thread.join()
return stdout_result + stderr_result
ваш код может потерять данные, когда подпроцесс завершается, в то время как некоторые данные находятся в буфере. Вместо этого читайте до EOF, см. teed_call ()
Используйте subprocess.call:
from subprocess import call
# Using list
call(["echo", "Hello", "world"])
# Single string argument varies across platforms so better split it
call("echo Hello world".split(" "))
Я обычно использую подпроцесс вместе с шлекс (для обработки экранирования строк в кавычках):
>>> import subprocess, shlex
>>> command = 'ls -l "/your/path/with spaces/"'
>>> call_params = shlex.split(command)
>>> print call_params
["ls", "-l", "/your/path/with spaces/"]
>>> subprocess.call(call_params)
Бесстыдный штекер, я написал для этого библиотеку: P https://github.com/houqp/shell.py
На данный момент это в основном обертка для popen и shlex. Он также поддерживает команды конвейера, поэтому вы можете упростить цепочку команд в Python. Таким образом, вы можете делать такие вещи, как:
ex('echo hello shell.py') | "awk '{print }'"
Простой способ - использовать модуль ОС:
import os
os.system('ls')
В качестве альтернативы вы также можете использовать модуль подпроцесса:
import subprocess
subprocess.check_call('ls')
Если вы хотите, чтобы результат сохранялся в переменной, попробуйте:
import subprocess
r = subprocess.check_output('ls')
Также есть Отвес
>>> from plumbum import local
>>> ls = local["ls"]
>>> ls
LocalCommand(<LocalPath /bin/ls>)
>>> ls()
u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n'
>>> notepad = local["c:\windows\notepad.exe"]
>>> notepad() # Notepad window pops up
u'' # Notepad window is closed by user, command returns
Использовать:
import os
cmd = 'ls -al'
os.system(cmd)
os - этот модуль обеспечивает переносимый способ использования функций, зависящих от операционной системы.
Для других функций os в документации есть здесь.
он также устарел. использовать подпроцесс
Использование функции Popen модуля subprocess Python - самый простой способ запуска команд Linux. В этом случае функция Popen.communicate() выдаст ваши команды. Например
import subprocess
..
process = subprocess.Popen(..) # Pass command and arguments to the function
stdout, stderr = process.communicate() # Get command output and error
..
Это уже неверно, и, вероятно, не было, когда был опубликован этот ответ. Вам следует предпочесть subprocess.check_call() и его друзьям, если вам не нужен более низкий уровень управления более сложным Popen(). В последних версиях Python популярной рабочей лошадкой является subprocess.run().
Вот мои два цента: На мой взгляд, это лучшая практика при работе с внешними командами ...
Это возвращаемые значения из метода execute ...
pass, stdout, stderr = execute(["ls","-la"],"/home/user/desktop")
Это метод выполнения ...
def execute(cmdArray,workingDir):
stdout = ''
stderr = ''
try:
try:
process = subprocess.Popen(cmdArray,cwd=workingDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1)
except OSError:
return [False, '', 'ERROR : command(' + ' '.join(cmdArray) + ') could not get executed!']
for line in iter(process.stdout.readline, b''):
try:
echoLine = line.decode("utf-8")
except:
echoLine = str(line)
stdout += echoLine
for line in iter(process.stderr.readline, b''):
try:
echoLine = line.decode("utf-8")
except:
echoLine = str(line)
stderr += echoLine
except (KeyboardInterrupt,SystemExit) as err:
return [False,'',str(err)]
process.stdout.close()
returnCode = process.wait()
if returnCode != 0 or stderr != '':
return [False, stdout, stderr]
else:
return [True, stdout, stderr]
Потенциал взаимоблокировки: вместо этого используйте метод .communicate
А еще лучше избегать Popen() и использовать API более высокого уровня, который теперь объединен в единую функцию subprocess.run().
Для Python 3.5+ рекомендуется использовать запустить функцию из модуля подпроцесса. Это возвращает объект CompletedProcess, из которого вы можете легко получить выходные данные, а также код возврата.
from subprocess import PIPE, run
command = ['echo', 'hello']
result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(result.returncode, result.stdout, result.stderr)
ответ с функцией запуска был добавлен в 2015 году. Ты повторил это. Я думаю, это была причина голосования против
Я бы порекомендовал следующий метод «запустить», который поможет нам получить STDOUT, STDERR и статус выхода в виде словаря; Вызывающий это может прочитать словарь, возвращаемый методом run, чтобы узнать фактическое состояние процесса.
def run (cmd):
print "+ DEBUG exec({0})".format(cmd)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True)
(out, err) = p.communicate()
ret = p.wait()
out = filter(None, out.split('\n'))
err = filter(None, err.split('\n'))
ret = True if ret == 0 else False
return dict({'output': out, 'error': err, 'status': ret})
#end
Это не полностью переопределяет что-то вроде subprocess.run(). Вам особенно следует избегать shell=True, когда это не является строго необходимым.
В Windows вы можете просто импортировать модуль subprocess и запускать внешние команды, вызывая subprocess.Popen(), subprocess.Popen().communicate() и subprocess.Popen().wait(), как показано ниже:
# Python script to run a command line
import subprocess
def execute(cmd):
"""
Purpose : To execute a command and return exit status
Argument : cmd - command to execute
Return : exit_code
"""
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(result, error) = process.communicate()
rc = process.wait()
if rc != 0:
print "Error: failed to execute command:", cmd
print error
return result
# def
command = "tasklist | grep python"
print "This process detail: \n", execute(command)
Выход:
This process detail:
python.exe 604 RDP-Tcp#0 4 5,660 K
Использовать:
import subprocess
p = subprocess.Popen("df -h", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")
Это дает хороший результат, с которым легче работать:
['Filesystem Size Used Avail Use% Mounted on',
'/dev/sda6 32G 21G 11G 67% /',
'none 4.0K 0 4.0K 0% /sys/fs/cgroup',
'udev 1.9G 4.0K 1.9G 1% /dev',
'tmpfs 387M 1.4M 386M 1% /run',
'none 5.0M 0 5.0M 0% /run/lock',
'none 1.9G 58M 1.9G 3% /run/shm',
'none 100M 32K 100M 1% /run/user',
'/dev/sda5 340G 222G 100G 69% /home',
'']
Чтобы получить идентификатор сети из OpenStackНейтрон:
#!/usr/bin/python
import os
netid = "nova net-list | awk '/ External / { print }'"
temp = os.popen(netid).read() /* Here temp also contains new line (\n) */
networkId = temp.rstrip()
print(networkId)
Выход nova net-list
+--------------------------------------+------------+------+
| ID | Label | CIDR |
+--------------------------------------+------------+------+
| 431c9014-5b5d-4b51-a357-66020ffbb123 | test1 | None |
| 27a74fcd-37c0-4789-9414-9531b7e3f126 | External | None |
| 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None |
| 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal | None |
+--------------------------------------+------------+------+
Выход печать (networkId)
27a74fcd-37c0-4789-9414-9531b7e3f126
Не рекомендуется рекомендовать os.popen() в 2016 году. Скрипт Awk можно легко заменить собственным кодом Python.
Есть много способов вызвать команду.
если and.exe требуется два параметра. В cmd мы можем вызвать sample.exe, используя это:
and.exe 2 3, и на экране отображается 5.
Если мы используем сценарий Python для вызова and.exe, мы должны сделать что-то вроде ..
os.system(cmd,...)
os.system(("and.exe" + " " + "2" + " " + "3"))os.popen(cmd,...)
os.popen(("and.exe" + " " + "2" + " " + "3"))subprocess.Popen(cmd,...)subprocess.Popen(("and.exe" + " " + "2" + " " + "3"))Это слишком сложно, поэтому мы можем объединить cmd с пробелом:
import os
cmd = " ".join(exename,parameters)
os.popen(cmd)
os.popen не следует рекомендовать и, возможно, даже упоминать. В примере subpocess аргументы следует передавать в виде списка, а не объединять их пробелами.
Вот вызов внешней команды и возврат или печать вывода команды:
Python Подпроцесс check_output подходит для
Run command with arguments and return its output as a byte string.
import subprocess
proc = subprocess.check_output('ipconfig /all')
print proc
Аргумент должен быть правильно токенизирован в список, или вы должны явно передать shell=True. В Python 3.x (где x> 3, я думаю) вы можете получить вывод как правильную строку с universal_newlines=True, и вы, вероятно, захотите переключиться на subproces.run()
Существует множество различных библиотек, которые позволяют вызывать внешние команды с помощью Python. Для каждой библиотеки я дал описание и показал пример вызова внешней команды. В качестве примера я использовал команду ls -l (перечислить все файлы). Если вы хотите узнать больше о любой из библиотек, я перечислил и связал документацию для каждой из них.
Sources:
These are all the libraries:
Надеюсь, это поможет вам решить, какую библиотеку использовать :)
subprocess
Подпроцесс позволяет вызывать внешние команды и подключать их к их каналам ввода / вывода / ошибок (stdin, stdout и stderr). Подпроцесс - это выбор по умолчанию для выполнения команд, но иногда лучше подходят другие модули.
subprocess.run(["ls", "-l"]) # Run command
subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command
os
os используется для «функциональности, зависящей от операционной системы». Его также можно использовать для вызова внешних команд с помощью os.system и os.popen (Примечание: существует также subprocess.popen). os всегда будет запускать оболочку и является простой альтернативой для людей, которым это не нужно или которые не знают, как использовать subprocess.run.
os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output
sh
sh - это интерфейс подпроцесса, который позволяет вам вызывать программы, как если бы они были функциями. Это полезно, если вы хотите запускать команду несколько раз.
sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function
plumbum
plumbum - это библиотека для "скриптовых" программ Python. Вы можете вызывать программы как функции, как в sh. Plumbum полезен, если вы хотите запустить конвейер без оболочки.
ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command
pexpect
pexpect позволяет создавать дочерние приложения, управлять ими и находить закономерности в их выводе. Это лучшая альтернатива подпроцессу для команд, ожидающих tty в Unix.
pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo [email protected]:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')
fabric
fabric - это библиотека Python 2.5 и 2.7. Он позволяет выполнять локальные и удаленные команды оболочки. Fabric - простая альтернатива для запуска команд в защищенной оболочке (SSH).
fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output
envoy
envoy известен как «подпроцесс для людей». Он используется как удобная оболочка для модуля subprocess.
r = envoy.run("ls -l") # Run command
r.std_out # get output
commands
commands содержит функции оболочки для os.popen, но он был удален из Python 3, поскольку subprocess - лучшая альтернатива.
Редактирование было основано на комментарии Дж. Ф. Себастьяна.
В Linux, если вы хотите вызвать внешнюю команду, которая будет выполняться независимо (будет продолжать работать после завершения скрипта python), вы можете использовать простую очередь как диспетчер очереди задач или команду в
Пример с диспетчером очереди задач:
import os
os.system('ts <your-command>')
Примечания о диспетчере очереди задач (ts):
Вы можете установить количество одновременно запускаемых процессов ("слотов") с помощью:
ts -S <number-of-slots>
Для установки ts не требуются права администратора. Вы можете загрузить и скомпилировать его из исходников с помощью простого make, добавить его в свой путь, и все готово.
ts не является стандартом ни в одном известном мне дистрибутиве, хотя указатель на at в некоторой степени полезен. Вы, наверное, также должны упомянуть batch. Как и везде, в рекомендации os.system(), вероятно, следует хотя бы упомянуть, что subprocess является рекомендуемой заменой.
Calling an external command in Python
Просто используйте subprocess.run, который возвращает объект CompletedProcess:
>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)
Начиная с Python 3.5, в документации рекомендуется subprocess.run:
The recommended approach to invoking subprocesses is to use the run() function for all use cases it can handle. For more advanced use cases, the underlying Popen interface can be used directly.
Вот пример простейшего возможного использования - и он работает именно так:
>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)
run ожидает успешного завершения команды, а затем возвращает объект CompletedProcess. Вместо этого он может поднять TimeoutExpired (если вы дадите ему аргумент timeout=) или CalledProcessError (если он не работает и вы передаете check=True).
Как вы можете заключить из приведенного выше примера, оба stdout и stderr по умолчанию передаются на ваш собственный stdout и stderr.
Мы можем проверить возвращенный объект и увидеть заданную команду и код возврата:
>>> completed_process.args
'python --version'
>>> completed_process.returncode
0
Если вы хотите захватить вывод, вы можете передать subprocess.PIPE соответствующему stderr или stdout:
>>> cp = subprocess.run('python --version',
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''
(Мне кажется интересным и немного противоречащим интуиции, что информация о версии помещается в stderr вместо stdout.)
Можно легко перейти от предоставления командной строки вручную (как предполагает вопрос) к предоставлению строки, созданной программно. Не создавайте строки программно. Это потенциальная проблема безопасности. Лучше предположить, что вы не доверяете вводимым данным.
>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = subprocess.run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\r\n This is indented.\r\n'
Обратите внимание: только args следует передавать позиционно.
Вот фактическая подпись в источнике и как показано help(run):
def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
popenargs и kwargs передаются конструктору Popen. input может быть строкой байтов (или Unicode, если указать кодировку или universal_newlines=True), которые будут переданы на стандартный ввод подпроцесса.
Документация описывает timeout= и check=True лучше, чем я мог бы:
The timeout argument is passed to Popen.communicate(). If the timeout expires, the child process will be killed and waited for. The TimeoutExpired exception will be re-raised after the child process has terminated.
If check is true, and the process exits with a non-zero exit code, a CalledProcessError exception will be raised. Attributes of that exception hold the arguments, the exit code, and stdout and stderr if they were captured.
и этот пример для check=True лучше, чем тот, который я мог придумать:
>>> subprocess.run("exit 1", shell=True, check=True) Traceback (most recent call last): ... subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
Вот расширенная подпись, как указано в документации:
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None)
Обратите внимание, что это означает, что позиционно следует передавать только список аргументов. Поэтому передайте оставшиеся аргументы как аргументы ключевого слова.
Когда вместо этого использовать Popen? Я бы изо всех сил пытался найти вариант использования, основанный только на аргументах. Однако прямое использование Popen предоставит вам доступ к его методам, включая poll, send_signal, terminate и wait.
Вот подпись Popen, как указано в источник. Я думаю, что это наиболее точная инкапсуляция информации (в отличие от help(Popen)):
def __init__(self, args, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
shell=False, cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
pass_fds=(), *, encoding=None, errors=None):
Но информативнее документация Popen:
subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None)Execute a child program in a new process. On POSIX, the class uses os.execvp()-like behavior to execute the child program. On Windows, the class uses the Windows CreateProcess() function. The arguments to Popen are as follows.
Понимание остальной документации по Popen будет оставлено читателю в качестве упражнения.
Здесь можно найти простой пример двусторонней связи между первичным процессом и подпроцессом: stackoverflow.com/a/52841475/1349673
В первом примере, вероятно, должен быть shell=True или (что еще лучше) передать команду в виде списка.
Я написал оболочку для обработки ошибок, перенаправления вывода и прочего.
import shlex
import psutil
import subprocess
def call_cmd(cmd, stdout=sys.stdout, quiet=False, shell=False, raise_exceptions=True, use_shlex=True, timeout=None):
"""Exec command by command line like 'ln -ls "/var/log"'
"""
if not quiet:
print("Run %s", str(cmd))
if use_shlex and isinstance(cmd, (str, unicode)):
cmd = shlex.split(cmd)
if timeout is None:
process = subprocess.Popen(cmd, stdout=stdout, stderr=sys.stderr, shell=shell)
retcode = process.wait()
else:
process = subprocess.Popen(cmd, stdout=stdout, stderr=sys.stderr, shell=shell)
p = psutil.Process(process.pid)
finish, alive = psutil.wait_procs([p], timeout)
if len(alive) > 0:
ps = p.children()
ps.insert(0, p)
print('waiting for timeout again due to child process check')
finish, alive = psutil.wait_procs(ps, 0)
if len(alive) > 0:
print('process {} will be killed'.format([p.pid for p in alive]))
for p in alive:
p.kill()
if raise_exceptions:
print('External program timeout at {} {}'.format(timeout, cmd))
raise CalledProcessTimeout(1, cmd)
retcode = process.wait()
if retcode and raise_exceptions:
print("External program failed %s", str(cmd))
raise subprocess.CalledProcessError(retcode, cmd)
Вы можете назвать это так:
cmd = 'ln -ls "/var/log"'
stdout = 'out.txt'
call_cmd(cmd, stdout)
В качестве примера (в Linux):
import subprocess
subprocess.run('mkdir test.dir', shell=True)
Это создает test.dir в текущем каталоге. Обратите внимание, что это тоже работает:
import subprocess
subprocess.call('mkdir test.dir', shell=True)
Эквивалентный код с использованием os.system:
import os
os.system('mkdir test.dir')
Лучшей практикой было бы использовать subprocess вместо os, при этом .run предпочтительнее .call. Все, что вам нужно знать о подпроцессе, - это здесь. Также обратите внимание, что вся документация Python доступна для загрузки с здесь. Я скачал PDF в формате .zip. Я упоминаю об этом, потому что есть хороший обзор модуля os в tutorial.pdf (стр. 81). Кроме того, это авторитетный ресурс для программистов Python.
Согласно docs.python.org/2/library/…, "shell = True" может вызвать проблемы с безопасностью.
@Nick Predley: отмечено, но "shell = False" не выполняет желаемую функцию. В чем конкретно заключаются проблемы безопасности и какая альтернатива? Пожалуйста, дайте мне знать как можно скорее: я не хочу публиковать что-либо, что может вызвать проблемы у всех, кто это просматривает.
Основное предупреждение находится в документации, но этот вопрос объясняет его более подробно: stackoverflow.com/questions/3172470/…
Часто я использую следующую функцию для внешних команд, и это особенно удобно для длительные процессы. Приведенный ниже метод вывод процесса хвостовпока работает и возвращает результат, вызывает исключение, если процесс завершился ошибкой.
Это выходит, если процесс выполняется с использованием poll () в процессе.
import subprocess,sys
def exec_long_running_proc(command, args):
cmd = "{} {}".format(command, " ".join(str(arg) if ' ' not in arg else arg.replace(' ','\ ') for arg in args))
print(cmd)
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Poll process for new output until finished
while True:
nextline = process.stdout.readline().decode('UTF-8')
if nextline == '' and process.poll() is not None:
break
sys.stdout.write(nextline)
sys.stdout.flush()
output = process.communicate()[0]
exitCode = process.returncode
if (exitCode == 0):
return output
else:
raise Exception(command, exitCode, output)
Вы можете вызвать его так:
exec_long_running_proc(command = "hive", args=["-f", hql_path])
Вы получите неожиданные результаты, передав аргумент с пробелом. Использование repr(arg) вместо str(arg) может помочь по простому совпадению, поскольку python и sh экранируют кавычки одинаково
@sbk repr(arg) действительно не помог, приведенный выше код также обрабатывает пробелы. Сейчас работает следующая exec_long_running_proc(command = "ls", args=["-l", "~/test file*"])
Вызов внешней команды в Python
Простой способ вызвать внешнюю команду - использовать os.system(...). И эта функция возвращает значение выхода команды. Но недостаток в том, что мы не получим stdout и stderr.
ret = os.system('some_cmd.sh')
if ret != 0 :
print 'some_cmd.sh execution returned failure'
Вызов внешней команды в Python в фоновом режиме
subprocess.Popen обеспечивает большую гибкость для запуска внешней команды, чем использование os.system. Мы можем запустить команду в фоновом режиме и дождаться ее завершения. И после этого мы можем получить stdout и stderr.
proc = subprocess.Popen(["./some_cmd.sh"], stdout=subprocess.PIPE)
print 'waiting for ' + str(proc.pid)
proc.wait()
print 'some_cmd.sh execution finished'
(out, err) = proc.communicate()
print 'some_cmd.sh output : ' + out
Вызов длительной внешней команды в Python в фоновом режиме и остановка через некоторое время
Мы даже можем запустить длительный процесс в фоновом режиме с помощью subprocess.Popen и убить его через некоторое время, как только его задача будет выполнена.
proc = subprocess.Popen(["./some_long_run_cmd.sh"], stdout=subprocess.PIPE)
# Do something else
# Now some_long_run_cmd.sh exeuction is no longer needed, so kill it
os.system('kill -15 ' + str(proc.pid))
print 'Output : ' proc.communicate()[0]
Это может быть так просто:
import os
cmd = "your command"
os.system(cmd)
Это не указывает на недостатки, которые более подробно объясняются в PEP-324. Документация для os.system явно рекомендует избегать этого в пользу subprocess.
Если вам нужно вызвать команду оболочки из записной книжки Python (например, Юпитер, Zeppelin, Databricks или Google Cloud Datalab), вы можете просто использовать префикс !.
Например,
!ls -ilF
Вызвать - это инструмент и библиотека для выполнения задач Python (2.7 и 3.4+). Он предоставляет чистый высокоуровневый API для запуска команд оболочки.
>>> from invoke import run
>>> cmd = "pip install -r requirements.txt"
>>> result = run(cmd, hide=True, warn=True)
>>> print(result.ok)
True
>>> print(result.stdout.splitlines()[-1])
Successfully installed invocations-0.13.0 pep8-1.5.7 spec-1.3.1
Это отличная библиотека. На днях я пытался объяснить это коллеге, и я описал это так: invoke соответствует subprocess, как requests соответствует urllib3.
Я написал небольшую библиотеку, чтобы помочь с этим вариантом использования:
https://pypi.org/project/citizenshell/
Его можно установить с помощью
pip install citizenshell
А потом использовали так:
from citizenshell import sh
assert sh("echo Hello World") == "Hello World"
Вы можете отделить stdout от stderr и извлечь код выхода следующим образом:
result = sh(">&2 echo error && echo output && exit 13")
assert result.stdout() == ["output"]
assert result.stderr() == ["error"]
assert result.exit_code() == 13
И что самое интересное, вам не нужно ждать завершения работы основной оболочки, прежде чем начать обработку вывода:
for line in sh("for i in 1 2 3 4; do echo -n 'It is '; date +%H:%M:%S; sleep 1; done", wait=False)
print ">>>", line + "!"
напечатает строки, поскольку они доступны благодаря wait = False
>>> It is 14:24:52!
>>> It is 14:24:53!
>>> It is 14:24:54!
>>> It is 14:24:55!
Больше примеров можно найти на https://github.com/meuter/citizenshell
Поскольку некоторые ответы были связаны с предыдущими версиями Python или использовали модуль os.system, я публикую этот ответ для таких людей, как я, которые намереваются использовать subprocess в Python 3.5 +. Следующее помогло мне в Linux:
import subprocess
# subprocess.run() returns a completed process object that can be inspected
c = subprocess.run(["ls", "-ltrh"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(c.stdout.decode('utf-8'))
Как упомянуто в документация, значения PIPE представляют собой байтовые последовательности, и для их правильного отображения следует рассмотреть возможность декодирования. Для более поздних версий Python text=True и encoding='utf-8' добавлены в kwargs subprocess.run().
Результатом вышеупомянутого кода является:
total 113M
-rwxr-xr-x 1 farzad farzad 307 Jan 15 2018 vpnscript
-rwxrwxr-x 1 farzad farzad 204 Jan 15 2018 ex
drwxrwxr-x 4 farzad farzad 4.0K Jan 22 2018 scripts
.... # Some other lines
Если вы нет и используете пользовательский ввод в командах, вы можете использовать это:
from os import getcwd
from subprocess import check_output
from shlex import quote
def sh(command):
return check_output(quote(command), shell=True, cwd=getcwd(), universal_newlines=True).strip()
И использовать его как
branch = sh('git rev-parse --abbrev-ref HEAD')
shell=True создаст оболочку, поэтому вы можете использовать pipe и подобные оболочки sh('ps aux | grep python'). Это очень удобно для выполнения жестко запрограммированных команд и обработки их вывода. universal_lines=True гарантирует, что выходные данные возвращаются в виде строки, а не в двоичном формате.
cwd=getcwd() будет следить за тем, чтобы команда запускалась в том же рабочем каталоге, что и интерпретатор. Это удобно для работы команд Git, как в приведенном выше примере имени ветки Git.
Некоторые рецепты
sh('free -m').split('\n')[1].split()[1]sh('df -m /').split('\n')[1].split()[4][0:-1]sum(map(float, sh('ps -ef -o pcpu').split('\n')[1:])Но это небезопасно для ввода пользователем, из документации:
Security Considerations
Unlike some other popen functions, this implementation will never implicitly call a system shell. This means that all characters, including shell metacharacters, can safely be passed to child processes. If the shell is invoked explicitly, via shell=True, it is the application’s responsibility to ensure that all whitespace and metacharacters are quoted appropriately to avoid shell injection vulnerabilities.
When using shell=True, the shlex.quote() function can be used to properly escape whitespace and shell metacharacters in strings that are going to be used to construct shell commands.
Даже при использовании shlex.quote() полезно сохранять небольшую паранойю при использовании пользовательского ввода в командах оболочки. Один из вариантов - использовать жестко запрограммированную команду для получения общего вывода и фильтрации по вводу пользователя. В любом случае использование shell=False гарантирует, что будет выполнен только тот процесс, который вы хотите выполнить, иначе вы получите ошибку No such file or directory.
Также есть некоторое влияние на производительность shell=True, из моих тестов кажется, что он примерно на 20% медленнее, чем shell=False (по умолчанию).
In [50]: timeit("check_output('ls -l'.split(), universal_newlines=True)", number=1000, globals=globals())
Out[50]: 2.6801227919995654
In [51]: timeit("check_output('ls -l', universal_newlines=True, shell=True)", number=1000, globals=globals())
Out[51]: 3.243950183999914
Султан - это недавний пакет, предназначенный для этой цели. Предоставляет некоторые тонкости управления привилегиями пользователей и добавление полезных сообщений об ошибках.
from sultan.api import Sultan
with Sultan.load(sudo=True, hostname = "myserver.com") as sultan:
sultan.yum("install -y tree").run()
Re "Обеспечивает некоторые тонкости": Вы можете уточнить?
Если вы пишете сценарий оболочки Python и в вашей системе установлен IPython, вы можете использовать префикс bang для запуска команды оболочки внутри IPython:
!ls
filelist = !ls
@PeterMortensen Я не думаю, что это работает в DOS, но должно работать в Cygwin.
import subprocess
p = subprocess.run(["ls", "-ltr"], capture_output=True)
print(p.stdout.decode(), p.stderr.decode())
os.popen() - самый простой и безопасный способ выполнить команду. Вы можете выполнить любую команду, которую запускаете в командной строке. Кроме того, вы также сможете захватить вывод команды с помощью os.popen().read().
Сделать это можно так:
import os
output = os.popen('Your Command Here').read()
print (output)
Пример, в котором вы перечисляете все файлы в текущем каталоге:
import os
output = os.popen('ls').read()
print (output)
# Outputs list of files in the directory
БОЛЬШИНСТВО СЛУЧАЕВ:
В большинстве случаев вам понадобится короткий фрагмент кода, подобный этому.:
import subprocess
import shlex
source = "test.txt"
destination = "test_copy.txt"
base = "cp {source} {destination}'"
cmd = base.format(source=source, destination=destination)
subprocess.check_call(shlex.split(cmd))
Это чисто и просто.
subprocess.check_callrun command with arguments and wait for command to complete.
shlex.splitsplit the string cmd using shell-like syntax
ОТДЫХ ДЕЛ:
Если это не работает для какой-то конкретной команды, скорее всего, у вас проблема с интерпретаторы командной строки. Операционная система выбрала программу по умолчанию, которая не подходит для вашего типа программы или не смогла найти подходящую в системном пути к исполняемому файлу.
Пример:
Использование оператора перенаправления в системе Unix
input_1 = "input_1.txt"
input_2 = "input_2.txt"
output = "merged.txt"
base_command = "/bin/bash -c 'cat {input} >> {output}'"
base_command.format(input_1, output=output)
subprocess.check_call(shlex.split(base_command))
base_command.format(input_2, output=output)
subprocess.check_call(shlex.split(base_command))
As it is stated in The Zen of Python: Explicit is better than implicit
Итак, если использовать функцию Python> = 3.6, это будет выглядеть примерно так:
import subprocess
import shlex
def run_command(cmd_interpreter: str, command: str) -> None:
base_command = f"{cmd_interpreter} -c '{command}'"
subprocess.check_call(shlex.split(base_command)
Существует несколько способов вызова внешней команды из Python. Есть несколько функций и модулей с хорошими вспомогательными функциями, которые могут сделать это действительно легко. Но среди всех рекомендуется модуль Subprocess.
import subprocess as s
s.call(["command.exe","..."])
Функция вызова запустит внешний процесс, передаст некоторые аргументы командной строки и дождется его завершения. Когда он закончится, вы продолжите выполнение. Аргументы в функции Call передаются по списку. Первым аргументом в списке является команда, обычно в форме исполняемого файла, а последующие аргументы в списке - все, что вы хотите передать.
Если вы вызывали процессы из командной строки в окнах, прежде чем вы узнаете, что вам часто нужно заключать аргументы в кавычки. Вам нужно поставить кавычки вокруг него, если есть пробел, тогда есть обратная косая черта и есть некоторые сложные правила, но вы можете избежать всего этого в python, используя модуль subprocess, потому что это список, и каждый элемент известен как отдельный и питон может правильно цитировать для вас.
В конце концов, после списка есть ряд необязательных параметров, один из которых - это оболочка, и если вы установите для оболочки значение true, ваша команда будет запущена, как если бы вы ввели ее в командной строке.
s.call(["command.exe","..."], shell=True)
Это дает вам доступ к таким функциям, как конвейер, вы можете перенаправлять на файлы, вы можете одновременно вызывать несколько команд.
Еще одна вещь, если ваш сценарий полагается на успешное выполнение процесса, тогда вы хотите проверить результат, и результат можно проверить с помощью вспомогательной функции вызова проверки.
s.check_call(...)
Это точно так же, как функция вызова, она принимает те же аргументы, принимает тот же список, вы можете передать любой из дополнительных аргументов, но она будет ждать завершения функций. И если код выхода функции отличен от нуля, это произойдет через исключение в скрипте python.
Наконец, если вам нужен более жесткий контроль, конструктор Popen, который также принадлежит модулю subprocess. Он также принимает те же аргументы, что и функция incall и check_call, но возвращает объект, представляющий запущенный процесс.
p=s.Popen("...")
Он не ожидает завершения запущенного процесса, также он не будет сразу генерировать какое-либо исключение, но он дает вам объект, который позволит вам делать такие вещи, как ждать его завершения, позволять вам общаться с ним, вы можете перенаправить стандартный ввод, стандартный вывод, если вы хотите отобразить вывод где-то еще, и многое другое.
я использую это для 3.6+
import subprocess
def execute(cmd):
"""
Purpose : To execute a command and return exit status
Argument : cmd - command to execute
Return : result, exit_code
"""
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(result, error) = process.communicate()
rc = process.wait()
if rc != 0:
print ("Error: failed to execute command: ", cmd)
print (error.rstrip().decode("utf-8"))
return result.rstrip().decode("utf-8"), serror.rstrip().decode("utf-8")
# def
Не используйте набор shell=True для запуска команд, он открывает программу для уязвимостей внедрения команд. Вы должны передать команду в виде списка с аргументами cmd=["/bin/echo", "hello word"]. docs.python.org/3/library/…
Начиная с Python 3.7.0 выпущен 27 июня 2018 г. (https://docs.python.org/3/whatsnew/3.7.html), вы можете достичь желаемого результата самым мощным и в то же время простым способом. Этот ответ призван вкратце показать вам основное резюме различных вариантов. Подробные ответы см. В других.
Большим преимуществом os.system(...) была его простота. subprocess лучше и по-прежнему прост в использовании, особенно с Python 3.5.
import subprocess
subprocess.run("ls -a", shell=True)
Примечание: Это точный ответ на ваш вопрос - выполнение команды
like in a shell
Если возможно, удалите служебные данные оболочки и запустите команду напрямую (требуется список).
import subprocess
subprocess.run(["help"])
subprocess.run(["ls", "-a"])
Передайте аргументы программы в виде списка. Не включайте экранирование \" для аргументов, содержащих пробелы.
Следующий код говорит сам за себя:
import subprocess
result = subprocess.run(["ls", "-a"], capture_output=True, text=True)
if "stackoverflow-logo.png" in result.stdout:
print("You're a fan!")
else:
print("You're not a fan?")
result.stdout - это обычный программный выход исключая ошибки. Прочтите result.stderr, чтобы получить их.
capture_output=True - включает захват. В противном случае result.stderr и result.stdout будут None. Доступно с Python 3.7.
text=True - удобный аргумент, добавленный в Python 3.7, который преобразует полученные двоичные данные в строки Python, с которыми вы можете легко работать.
Делать
if result.returncode == 127: print("The program failed for some weird reason")
elif result.returncode == 0: print("The program succeeded")
else: print("The program failed unexpectedly")
Если вы просто хотите проверить, удалось ли выполнить программу (код возврата == 0), а в противном случае выбросить исключение, есть более удобная функция:
result.check_returncode()
Но это Python, поэтому есть еще более удобный аргумент check, который автоматически делает то же самое за вас:
result = subprocess.run(..., check=True)
Возможно, вы захотите, чтобы весь вывод программы был внутри стандартного вывода, даже ошибки. Для этого запустите
result = subprocess.run(..., stderr=subprocess.STDOUT)
Тогда result.stderr будет None, а result.stdout будет содержать все.
shell=False ожидает список аргументов. Однако вы можете самостоятельно разделить строку аргумента с помощью shlex.
import subprocess
import shlex
subprocess.run(shlex.split("ls -a"))
Вот и все.
Скорее всего, вы только что начали использовать Python, когда столкнетесь с этим вопросом. Давайте посмотрим на некоторые общие проблемы.
FileNotFoundError: [Errno 2] No such file or directory: 'ls -a': 'ls -a'
Вы запускаете подпроцесс без shell=True. Либо используйте список (["ls", "-a"]), либо установите shell=True.
TypeError: [...] NoneType [...]
Убедитесь, что вы установили capture_output=True.
TypeError: a bytes-like object is required, not [...]
Вы всегда получаете результат в байтах от вашей программы. Если вы хотите работать с ним как с обычной строкой, установите text=True.
subprocess.CalledProcessError: Command '[...]' returned non-zero exit status 1.
Ваша команда не была успешно выполнена. Вы можете отключить проверку кода возврата или проверить действительность вашей программы.
TypeError: init() got an unexpected keyword argument [...]
Скорее всего, вы используете версию Python старше 3.7.0; обновите его до самого последнего доступного. В противном случае в этом сообщении StackOverflow есть другие ответы, показывающие вам более старые альтернативные решения.
Понятия не имею, что я имел в виду почти десять лет назад (проверьте дату!), Но если бы я должен был догадаться, это было бы так, что не было сделано никакой проверки.