Вызов внешней команды из Python


4887

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

Ответы:


4697

Посмотрите на модуль подпроцесса в стандартной библиотеке:

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

Преимущество subprocessпротив прогноза в systemтом , что она является более гибкой (вы можете получить stdout, stderr, «реальный» код состояния, лучше обработка ошибок, и т.д ...).

Официальная документация рекомендует subprocessмодуль над альтернативой os.system():

subprocessМодуль предоставляет более мощные средства для порождения новых процессов и получения их результатов; использование этого модуля предпочтительнее, чем использование этой функции [ os.system()].

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

Для версий Python до 3.5 используйте call:

import subprocess
subprocess.call(["ls", "-l"])

Есть ли способ использовать подстановку переменных? IE я пытался сделать echo $PATHс помощью call(["echo", "$PATH"]), но он просто повторил буквальную строку $PATHвместо какой-либо замены. Я знаю, что могу получить переменную среды PATH, но мне интересно, есть ли простой способ заставить команду вести себя точно так же, как если бы я выполнял ее в bash.
Кевин Уилер

@KevinWheeler Вы должны использовать shell=Trueэто для работы.
SethMMorton

39
@KevinWheeler Вы НЕ должны использовать shell=True, для этого Python поставляется с os.path.expandvars . В вашем случае вы можете написать: os.path.expandvars("$PATH"). @SethMMorton, пожалуйста, пересмотрите свой комментарий -> Почему бы не использовать shell = True
Murmel

вызывает блок? то есть, если я хочу запустить несколько команд в forцикле, как мне это сделать без блокировки моего скрипта на python? Меня не волнует вывод команды, я просто хочу запустить их много.
Чарли Паркер

5
Если вы хотите создать список из команды с параметрами , список, который можно использовать с subprocessкогда shell=False, а затем используйте shlex.splitдля простого способа сделать это docs.python.org/2/library/shlex.html#shlex.split
Даниэль Ф

2985

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

  1. os.system("some_command with args")передает команду и аргументы в оболочку вашей системы. Это хорошо, потому что вы можете таким образом запускать несколько команд одновременно и настраивать каналы и перенаправление ввода / вывода. Например:

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

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

  1. stream = os.popen("some_command with args")будет делать то же самое, os.systemза исключением того, что он дает вам файловый объект, который вы можете использовать для доступа к стандартному вводу / выводу для этого процесса. Есть 3 других варианта popen, которые все обрабатывают ввод / вывод немного по-другому. Если вы передаете все как строку, то ваша команда передается в оболочку; если вы передаете их в виде списка, вам не нужно беспокоиться о том, чтобы избежать чего-либо. Смотрите документацию .

  2. 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-х различных попсовых функций. Смотрите документацию .

  3. callФункция от subprocessмодуля. Это в основном так же, как Popenкласс и принимает все те же аргументы, но он просто ждет, пока команда завершится, и даст вам код возврата. Например:

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

    Смотрите документацию .

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

  5. Модуль os также имеет все функции fork / exec / spawn, которые вы имели бы в программе на C, но я не рекомендую использовать их напрямую.

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

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

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

и представьте, что пользователь вводит что-то "моя мама не любит меня && rm -rf /", что может стереть всю файловую систему.


22
Хороший ответ / объяснение. Как этот ответ оправдывает девиз Python, описанный в этой статье? fastcompany.com/3026446/… «Стилистически, Perl и Python имеют разные философии. Самый известный девиз Perl:« Существует больше, чем один способ сделать это ». Python разработан так, чтобы иметь один очевидный способ сделать это». Кажется, так и должно быть. другой способ! В Perl я знаю только два способа выполнить команду - используя back-tick или open.
Жан

12
Если вы используете Python 3.5+, используйте subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run
Феникс

4
Обычно нужно знать, что делается с STDOUT и STDERR дочернего процесса, потому что, если они игнорируются, при некоторых (довольно распространенных) условиях, в конечном итоге дочерний процесс выдаст системный вызов для записи в STDOUT (тоже STDERR?) это превысило бы выходной буфер, предоставленный для процесса ОС, и ОС заставит его заблокировать, пока какой-либо процесс не прочитает из этого буфера. Итак, с помощью рекомендуемых в настоящее время способов, subprocess.run(..)что именно означает «Это не захватывает stdout или stderr по умолчанию». означают? А как насчет subprocess.check_output(..)STDERR?
Евгений Сергеев

2
@ Да, но это не то, что исполняется на примере. Заметьте, что echoперед строкой передано Popen? Так что полная команда будет echo my mama didnt love me && rm -rf /.
Крис Арндт

6
Это возможно неправильный путь назад. Большинству людей нужны только subprocess.run()его старшие братья subprocess.check_call()и сестры и соавт. Для случаев, когда этого недостаточно, см subprocess.Popen(). os.popen()возможно, вообще не следует упоминать или даже после "взломать свой собственный код fork / exec / spawn".
трипл

358

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

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().


44
.readlines()читает все строки одновременно, т. е. блокируется до выхода из подпроцесса (закрывает свой конец канала). Чтобы читать в режиме реального времени (если нет проблем с буферизацией), вы можете:for line in iter(p.stdout.readline, ''): print line,
jfs

1
Не могли бы вы уточнить, что вы подразумеваете под «если нет проблем с буферизацией»? Если процесс блокируется определенным образом, вызов подпроцесса также блокируется. То же самое может случиться и с моим оригинальным примером. Что еще может произойти в отношении буферизации?
EmmEff

15
дочерний процесс может использовать буферизацию блоков в неинтерактивном режиме вместо буферизации строк, поэтому p.stdout.readline()(примечание: нет sв конце) не увидит никаких данных, пока дочерний процесс не заполнит свой буфер Если ребенок не производит много данных, то вывод не будет в реальном времени. Смотрите вторую причину в вопросе: почему бы просто не использовать канал (popen ())? , В этом ответе приведены некоторые обходные пути (pexpect, pty, stdbuf)
jfs

4
вопрос буферизации имеет значение , только если вы хотите выхода в режиме реального времени и не относится к коду , который ничего не печатать , пока все данные не принимаются
JFS

3
Этот ответ был хорош для своего времени, но мы больше не должны рекомендовать Popenдля простых задач. Это также излишне указывает shell=True. Попробуйте один из subprocess.run()ответов.
tripleee

230

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

Предположим, вы хотите запустить длинную задачу из 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 ( "старт *"), os.spawnl с os.P_DETACH, subprocs и т.д.)
maranas

1
jfs

5
Неправильно указано следующее: «[o] n windows (win xp), родительский процесс не завершится, пока longtask.py не завершит свою работу». Родитель будет нормально завершать работу, но окно консоли (экземпляр conhost.exe) закрывается только при выходе из последнего присоединенного процесса, и дочерний объект может наследовать консоль родителя. Установка DETACHED_PROCESSв creationflagsизбегаешь этого пути предотвращения ребенка от наследования или создания консоли. Если вы вместо этого хотите новую консоль, используйте CREATE_NEW_CONSOLE(0x00000010).
Eryk Sun

1
Я не имел в виду, что выполнение как отдельного процесса некорректно. Тем не менее, вам может потребоваться установить стандартные дескрипторы для файлов, каналов или os.devnullпотому , что некоторые консольные программы завершают работу с ошибкой в ​​противном случае. Создайте новую консоль, когда вы хотите, чтобы дочерний процесс взаимодействовал с пользователем одновременно с родительским процессом. Было бы странно пытаться сделать оба в одном окне.
Eryk Sun

1
не существует ли независимого от ОС способа запуска процесса в фоновом режиме?
Чарли Паркер

152
import os
os.system("your command")

Обратите внимание, что это опасно, поскольку команда не очищена. Я оставляю это на ваше усмотрение в Google для получения соответствующей документации по модулям 'os' и 'sys'. Существует множество функций (exec * и spawn *), которые будут выполнять похожие действия.


6
Понятия не имею, что я имел в виду почти десять лет назад (проверь дату!), Но если бы мне пришлось угадывать, было бы, что проверка не была сделана.
nimish

1
Теперь это должно subprocessбыть несколько более универсальным и портативным решением. Выполнение внешних команд, конечно, по своей природе непереносимо (необходимо убедиться, что команда доступна для каждой архитектуры, которую необходимо поддерживать), а передача пользовательского ввода в качестве внешней команды небезопасна.
tripleee

1
Обратите внимание на отметку времени этого парня: «правильный» ответ имеет 40х голосов и является ответом № 1.
Nimish

Единственное решение, которое помогло мне запустить NodeJS.
Николай Шиндаров

148

Я бы порекомендовал использовать модуль подпроцесса вместо os.system, потому что он делает экранирование для вас и поэтому намного безопаснее.

subprocess.call(['ping', 'localhost'])

Если вы хотите создать список из команды с параметрами , список, который можно использовать с subprocessкогда shell=False, а затем используйте shlex.splitдля простого способа сделать это docs.python.org/2/library/shlex.html#shlex.split ( это рекомендуемый путь в соответствии с docs docs.python.org/2/library/subprocess.html#popen-constructor )
Даниэль Ф

6
Это неверно: « он спасается от оболочки и поэтому намного безопаснее ». подпроцесс не выполняет экранирование оболочки, субпроцесс не передает вашу команду через оболочку, поэтому нет необходимости экранировать оболочку.
Ли Райан

144
import os
cmd = 'ls -al'
os.system(cmd)

Если вы хотите вернуть результаты команды, вы можете использовать os.popen. Тем не менее, это не рекомендуется с версии 2.6 в пользу модуля подпроцесса , который хорошо освещены в других ответах.



Вы также можете сохранить свой результат с помощью вызова os.system, поскольку он работает как сама оболочка UNIX, например, os.system ('ls -l> test2.txt')
Стефан Грюнвальд,

97

Есть много разных библиотек, которые позволяют вам вызывать внешние команды с Python. Для каждой библиотеки я дал описание и показал пример вызова внешней команды. Команда, которую я использовал в качестве примера: ls -l(список всех файлов). Если вы хотите узнать больше о любой из библиотек, которые я перечислил, и связал документацию для каждой из них.

Источники:

Это все библиотеки:

Надеюсь, это поможет вам принять решение о том, какую библиотеку использовать :)

подпроцесс

Подпроцесс позволяет вам вызывать внешние команды и подключать их к их каналам ввода / вывода / ошибок (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.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.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 - это библиотека для скриптовых программ на 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 user@example.com:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')

ткань

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

эмиссар

Посланник известен как «подпроцесс для людей». Он используется как удобная обертка вокруг subprocessмодуля.

r = envoy.run("ls -l") # Run command
r.std_out # get output

команды

commandsсодержит функции-обертки для os.popen, но он был удален из Python 3, поскольку subprocessявляется лучшей альтернативой.

Редактирование было основано на комментарии Дж. Ф. Себастьяна.


74

Я всегда использую 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)

73

Проверьте также библиотеку Python "pexpect".

Он позволяет интерактивно управлять внешними программами / командами, даже ssh, ftp, telnet и т. Д. Вы можете просто напечатать что-то вроде:

child = pexpect.spawn('ftp 192.168.0.24')

child.expect('(?i)name .*: ')

child.sendline('anonymous')

child.expect('(?i)password')

70

Со стандартной библиотекой

Используйте модуль подпроцесса (Python 3):

import subprocess
subprocess.run(['ls', '-l'])

Это рекомендуемый стандартный способ. Тем не менее, более сложные задачи (каналы, вывод, ввод и т. Д.) Могут быть трудоемкими для создания и записи.

Примечание по версии Python: если вы все еще используете Python 2, subprocess.call работает аналогичным образом.

ProTip: shlex.split может помочь вам разобрать команду для run, callи другие subprocessфункции , в случае , если вы не хотите (или не можете!) Предоставлять их в виде списков:

import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))

С внешними зависимостями

Если вы не против внешних зависимостей, используйте plumbum :

from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())

Это лучшая subprocessобертка. Он кроссплатформенный, то есть работает как в Windows, так и в Unix-подобных системах. Установить с помощью pip install plumbum.

Другая популярная библиотека - это sh :

from sh import ifconfig
print(ifconfig('wlan0'))

Тем не менее, shотказались от поддержки Windows, так что это не так круто, как раньше. Установить с помощью pip install sh.


69

Если вам нужен вывод команды, которую вы вызываете, вы можете использовать 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'

Также обратите внимание на параметр оболочки .

Если оболочка есть True, указанная команда будет выполнена через оболочку. Это может быть полезно, если вы используете Python в основном для расширенного потока управления, который он предлагает для большинства системных оболочек, и при этом все еще хотите иметь удобный доступ к другим функциям оболочки, таким как каналы оболочки, подстановочные знаки имени файла, расширение переменных среды и расширение ~ до дома пользователя. каталог. Тем не менее, обратите внимание , что сам Python предлагает реализацию многих оболочечной подобные функции (в частности, glob, fnmatch, os.walk(), os.path.expandvars(), os.path.expanduser(), и shutil).


1
Обратите внимание, что check_outputтребуется список, а не строка. Если вы не полагаетесь на пробелы в кавычках, чтобы сделать ваш вызов действительным, самый простой и читаемый способ сделать это subprocess.check_output("ls -l /dev/null".split()).
Бруно Броноски

56

Вот как я выполняю свои команды. Этот код имеет все, что вам нужно в значительной степени

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()

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

54

Обновить:

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'>]


36

os.systemв порядке, но отчасти устаревший. Это также не очень безопасно. Вместо этого попробуйте subprocess. subprocessне вызывает sh напрямую и поэтому более безопасен, чем os.system.

Получите больше информации здесь .


2
Хотя я согласен с общей рекомендацией, subprocessон не устраняет все проблемы с безопасностью и имеет некоторые собственные неприятные проблемы.
tripleee

36

Вызов внешней команды в 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 :

Рекомендуемый подход к вызову подпроцессов заключается в использовании функции run () для всех случаев использования, которые она может обработать. Для более сложных вариантов использования базовый интерфейс Popen можно использовать напрямую.

Вот пример простейшего возможного использования - и он делает именно так, как просили:

>>> 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лучше, чем я мог:

Аргумент тайм-аута передается в Popen.communicate (). Если тайм-аут истекает, дочерний процесс будет остановлен и ожидает. Исключение TimeoutExpired будет повторно вызвано после завершения дочернего процесса.

Если проверка имеет значение true, и процесс завершается с ненулевым кодом завершения, будет вызвано исключение CalledProcessError. Атрибуты этого исключения содержат аргументы, код выхода и stdout и stderr, если они были захвачены.

и этот пример 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вместо? Я бы изо всех сил пытался найти вариант использования, основанный только на аргументах. Однако прямое использование 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)

Выполните дочернюю программу в новом процессе. В POSIX класс использует os.execvp () -подобное поведение для выполнения дочерней программы. В Windows класс использует функцию Windows CreateProcess (). Аргументы в пользу Попена следующие.

Понимание оставшейся документации Popenбудет оставлено читателю в качестве упражнения.


Простой пример двусторонней связи между основным процессом и подпроцессом можно найти здесь: stackoverflow.com/a/52841475/1349673
Джеймс Хиршорн,

Первый пример, вероятно, должен иметь shell=Trueили (еще лучше) передать команду в виде списка.
tripleee

33

Есть также Plumbum

>>> 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

28

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

import os

cmd = 'ls -al'

os.system(cmd)

OS - этот модуль предоставляет портативный способ использования функциональных возможностей, зависящих от операционной системы.

Для большего количества osфункций, вот документация.


2
это также устарело. использовать подпроцесс
Кори Голдберг

28

Это может быть так просто:

import os
cmd = "your command"
os.system(cmd)

1
Это не указывает на недостатки, которые более подробно объясняются в PEP-324 . Документация для os.systemявно рекомендует избегать его в пользу subprocess.
tripleee

25

Мне очень нравится 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

24

Здесь есть еще одно отличие, которое не упоминалось ранее.

subprocess.Popenвыполняет <команду> как подпроцесс. В моем случае мне нужно выполнить файл <a>, который должен взаимодействовать с другой программой, <b>.

Я попробовал подпроцесс, и выполнение прошло успешно. Однако <b> не смог связаться с <a>. Все нормально, когда я запускаю оба из терминала.

Еще одно: (ПРИМЕЧАНИЕ: kwrite ведет себя не так, как другие приложения. Если вы попробуете описанное ниже с Firefox, результаты не будут такими же.)

Если вы попытаетесь os.system("kwrite"), поток программы зависнет, пока пользователь не закроет kwrite. Чтобы преодолеть это, я попытался вместо этого os.system(konsole -e kwrite). На этот раз программа продолжала работать, но kwrite стал подпроцессом консоли.

Любой, кто запускает kwrite, не является подпроцессом (т. Е. В системном мониторе он должен отображаться у самого левого края дерева).


1
Что вы подразумеваете под "Кто-нибудь запускает kwrite, не будучи подпроцессом" ?
Питер Мортенсен

23

os.systemне позволяет вам сохранять результаты, поэтому, если вы хотите сохранить результаты в каком-то списке или что-то, subprocess.callработает.


22

subprocess.check_callЭто удобно, если вы не хотите проверять возвращаемые значения. Выдает исключение при любой ошибке.


22

Я склонен использовать подпроцесс вместе с shlex (для обработки экранирования строк в кавычках):

>>> 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)

17

Бесстыдный плагин, я написал для этого библиотеку: P https://github.com/houqp/shell.py

Это в основном обертка для попен и шлекс на данный момент. Он также поддерживает команды конвейеризации, так что вы можете упростить цепочку команд в Python. Так что вы можете делать такие вещи, как:

ex('echo hello shell.py') | "awk '{print $2}'"

16

В 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

15

В Linux, если вы хотите вызвать внешнюю команду, которая будет выполняться независимо (будет продолжать работать после завершения сценария python), вы можете использовать простую очередь в качестве диспетчера очереди или в команде

Пример с диспетчером задач:

import os
os.system('ts <your-command>')

Примечания о диспетчере задач ( ts):

  1. Вы можете установить количество одновременных процессов («слотов») для запуска с помощью:

    ts -S <number-of-slots>

  2. Установка tsне требует прав администратора. Вы можете скачать и скомпилировать его из простого источника make, добавить его в свой путь, и все готово.


1
tsне является стандартным для любого дистрибутива, о котором я знаю, хотя указатель на atнего немного полезен. Вы должны также упомянуть batch. Как и везде, в os.system()рекомендации, вероятно, следует хотя бы упомянуть, что она subprocessявляется рекомендуемой заменой.
tripleee


15

Чтобы получить идентификатор сети из OpenStack Neutron :

#!/usr/bin/python
import os
netid = "nova net-list | awk '/ External / { print $2 }'"
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.
tripleee
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.