Вызов внешней команды в Python - PullRequest
4261 голосов
/ 18 сентября 2008

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

Ответы [ 56 ]

4103 голосов
/ 18 сентября 2008

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

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

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

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

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

Раздел " Замена старых функций на модуль подпроцесса " в документации подпроцесс может иметь несколько полезных рецептов.

Старые версии Python используют вызов:

import subprocess
subprocess.call(["ls", "-l"])
2765 голосов
/ 18 сентября 2008

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

305 голосов
/ 18 сентября 2008

Я обычно использую:

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

195 голосов
/ 12 февраля 2010

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

Предположим, что вы хотите запустить длинную задачу из 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 (win xp) родительский процесс не завершится, пока longtask.py не завершит свою работу. Это не то, что вы хотите в CGI-скрипте. Проблема не специфична для Python, в сообществе PHP проблемы одинаковы.

Решение состоит в том, чтобы передать DETACHED_PROCESS Флаг создания процесса в базовую функцию CreateProcess в win 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 еще не пролил свет.

119 голосов
/ 18 сентября 2008

Я бы рекомендовал использовать модуль подпроцесса вместо os.system, потому что он делает экранирование оболочки для вас и поэтому намного безопаснее: http://docs.python.org/library/subprocess.html

subprocess.call(['ping', 'localhost'])
118 голосов
/ 18 сентября 2008
import os
cmd = 'ls -al'
os.system(cmd)

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

114 голосов
/ 18 сентября 2008
import os
os.system("your command")

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

72 голосов
/ 29 октября 2016

Существует множество различных библиотек, которые позволяют вам вызывать внешние команды с помощью 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 используется для «функциональных возможностей, зависящих от операционной системы». Его также можно использовать для вызова внешних команд с os.system и os.popen (Примечание. Существует также подпроцесс.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

* 1083 Свинец *

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 является лучшей альтернативой.

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

64 голосов
/ 13 марта 2012

Я всегда использую 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)
62 голосов
/ 07 октября 2010

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

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

child = pexpect.spawn('ftp 192.168.0.24')

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

child.sendline('anonymous')

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