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




запуск bash скрипта из python (24)

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

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

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


Вот как я запускаю свои команды. Этот код содержит все необходимое

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.check_call удобен, если вы не хотите проверять возвращаемые значения. Он генерирует исключение при любой ошибке.


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

Вы можете использовать Popen, а затем вы можете проверить статус процедуры:

from subprocess import Popen

proc = Popen(['ls', '-l'])
if proc.poll() is None:
    proc.kill()

Проверьте subprocess.Popen .


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

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



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

#!/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

Я имею тенденцию использовать подпроцесс вместе с 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)

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

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

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

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

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

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

import subprocess
import sys

# some code here

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

# some more code here

Идея здесь заключается в том, что вы не хотите ждать в строке «подпроцесс вызова», пока не закончится longtask.py. Но неясно, что происходит после строки «еще один код здесь» из примера.

Моя целевая платформа была бесплатной, но разработка была на окнах, поэтому сначала я столкнулся с проблемой в 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. Если кто-нибудь знает, пожалуйста, поделитесь своими идеями. Googling при запуске фоновых процессов в Python еще не проливает свет.


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

Получите дополнительную информацию here .


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

import os

cmd = 'ls -al'

os.system(cmd)

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

Для более функций os here приведена документация.


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


Здесь есть еще одна разница, которая не упоминается ранее.

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

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

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

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

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


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

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

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

Заметки о диспетчере очереди задач ( ts):

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

    ts -S <number-of-slots>

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


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

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

Модуль подпроцесса использования (Python 3):

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

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

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

ProTip: docs.python.org/2/library/shlex.html#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 .


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

  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, которые все обрабатывают i / o немного по-другому. Если вы передаете все как строку, ваша команда передается в оболочку; если вы передадите их в список, то вам не нужно беспокоиться о том, чтобы избежать чего-либо. См. Документацию .

  3. Класс subprocess модуля 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. См. Документацию .

  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 /».


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

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


Вызов внешней команды в 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 и popenargs присваиваются конструктору Popen . input может быть строкой байтов (или unicode, если указать кодировку или universal_newlines=True ), которые будут переданы по каналу в stdin подпроцесса.

В документации описывается timeout= и check=True лучше, чем я мог:

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

Если проверка верна, и процесс завершается с ненулевым кодом выхода, будет вызвано исключение 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 будет оставлено в качестве упражнения для читателя.


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

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

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


Без вывода результата:

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

С выходом результата:

import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")

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

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




external