Вступление
Python предлагает несколько вариантов запуска внешних процессов и
взаимодействия с операционной системой. Однако методы отличаются для
Python 2 и 3. Python 2 имеет несколько методов в os
, которые теперь
устарели и заменены subprocess
, который является предпочтительным
вариантом в Python 3.
В этой статье мы будем говорить о различных os
и subprocess
методов,
как использовать их, как они отличаются друг от друга, на какую версию
Python они должны быть использованы, и даже , как конвертировать старые
команды новее единицы.
Надеюсь, к концу этой статьи вы лучше поймете, как вызывать внешние команды из кода Python и какой метод следует использовать для этого.
Прежде всего, это старые методы os.popen*
Методы os.popen *
Модуль os
предлагает четыре различных метода, которые позволяют нам
взаимодействовать с операционной системой (как и с командной строкой) и
создавать канал для
других команд. Я имею в виду следующие методы: popen
, popen2
,
popen3
и popen4
, все из которых описаны в следующих разделах.
Цель каждого из этих методов - иметь возможность вызывать другие
программы из вашего кода Python. Это может быть вызов другого
исполняемого файла, например вашей собственной скомпилированной
программы на C ++, или команды оболочки, например ls
или mkdir
.
os.popen
Метод os.popen
открывает канал из команды. Этот канал позволяет
команде отправлять свои выходные данные другой команде. Результатом
является открытый файл, к которому могут получить доступ другие
программы.
Синтаксис следующий:
os.popen(command[, mode[, bufsize]])
Здесь command
- это то, что вы будете выполнять, и его вывод будет
доступен через открытый файл. Аргумент mode
определяет , является ли
считываемый этого выходного файла ( «г») или перезаписываемый ( «W»).
Добавление «b» к mode
откроет файл в двоичном режиме. Таким образом,
например, «rb» создаст читаемый объект двоичного файла.
Чтобы получить код выхода выполненной команды, вы должны использовать
метод close()
файлового объекта.
Параметр bufsize
сообщает popen
сколько данных следует буферизовать,
и может принимать одно из следующих значений:
- 0 = без буферизации (значение по умолчанию)
- 1 = строчная буферизация
- N = приблизительный размер буфера, когда N> 0; и значение по умолчанию, когда N <0
Этот метод доступен для платформ Unix и Windows и устарел, начиная с
версии Python 2.6. Если вы в настоящее время используете этот метод и
хотите переключиться на версию Python 3, вот эквивалентная subprocess
для Python 3:
Методика Заменен на
труба = os.popen ('cmd', 'r', bufsize) pipe = Popen ('cmd', shell = True, bufsize = bufsize, stdout = PIPE) .stdout. труба = os.popen ('cmd', 'w', bufsize) pipe = Popen ('cmd', shell = True, bufsize = bufsize, stdin = PIPE) .stdin.
В приведенном ниже коде показан пример использования метода os.popen
import os
p = os.popen('ls -la')
print(p.read())
Приведенный выше код попросит операционную систему перечислить все файлы
в текущем каталоге. Результатом нашего метода, который хранится в p
,
является открытый файл, который читается и печатается в последней строке
кода. Результат этого кода (в контексте моего текущего каталога)
выглядит следующим образом:
$ python popen_test.py
total 32
drwxr-xr-x 7 scott staff 238 Nov 9 09:13 .
drwxr-xr-x 29 scott staff 986 Nov 9 09:08 ..
-rw-r--r-- 1 scott staff 52 Nov 9 09:13 popen2_test.py
-rw-r--r-- 1 scott staff 55 Nov 9 09:14 popen3_test.py
-rw-r--r-- 1 scott staff 53 Nov 9 09:14 popen4_test.py
-rw-r--r-- 1 scott staff 49 Nov 9 09:13 popen_test.py
-rw-r--r-- 1 scott staff 0 Nov 9 09:13 subprocess_popen_test.py
os.popen2
Этот метод очень похож на предыдущий. Основное различие заключается в том, что выводит метод. В этом случае он возвращает два файловых объекта, один для стандартного ввода, а другой файл для стандартного вывода .
Синтаксис следующий:
popen2(cmd[, mode[, bufsize]])
Эти аргументы имеют то же значение, что и в предыдущем методе os.popen
.
Метод popen2
доступен как для платформ Unix, так и для Windows. Однако
он присутствует только в Python 2. Опять же, если вы хотите использовать
subprocess
(более подробно показанную ниже), используйте вместо этого
следующее:
Методика Заменен на
(child_stdin, child_stdout) = p = Popen ('cmd', shell = True,
os.popen2 ('cmd', mode, bufsize) bufsize = bufsize, stdin = PIPE,
stdout = PIPE, close_fds = True)
(child_stdin, child_stdout) =
(p.stdin, p.stdout)
В приведенном ниже коде показан пример использования этого метода:
import os
in, out = os.popen2('ls -la')
print(out.read())
Этот код даст те же результаты, что и в первом выводе кода выше. Разница
здесь в том, что вывод popen2
состоит из двух файлов. Таким образом,
вторая строка кода определяет две переменные: in
и out
. В последней
строке, мы читаем выходной файл out
и распечатать его на консоль.
os.popen3
Этот метод очень похож на предыдущие. Однако разница в том, что вывод команды представляет собой набор из трех файлов: stdin, stdout и stderr .
Синтаксис:
os.popen3(cmd[, mode[, bufsize]])
где аргументы cmd
, mode
и bufsize
имеют те же характеристики, что
и в предыдущих методах. Метод доступен для платформ Unix и Windows.
Обратите внимание, что этот метод устарел, и документация Python
рекомендует нам заменить popen3
следующим образом:
Методика Заменен на
(child_stdin,\ p = Popen ('cmd', shell = True,
child_stdout,\ bufsize = bufsize,
child_stderr) = os.popen3 ('cmd', stdin = PIPE, stdout = PIPE, stderr
mode, bufsize) = PIPE, close_fds = True)
(child_stdin,
child_stdout,
child_stderr) = (p.stdin, p.stdout,
p.stderr)
Как и в предыдущих примерах, приведенный ниже код даст тот же результат, что и в нашем первом примере.
import os
in, out, err = os.popen3('ls -la')
print(out.read())
Однако в этом случае мы должны определить три файла: stdin, stdout и
stderr. Список файлов из нашей команды ls -la
сохраняется в файле
out
os.popen4
Как вы, наверное, догадались, os.popen4
похож на предыдущие методы.
Однако в этом случае он возвращает только два файла: один для
стандартного ввода, а другой - для стандартного вывода и стандартного
вывода.
Этот метод доступен для платформ Unix и Windows и ( сюрприз! ) Также
устарел, начиная с версии 2.6. Чтобы заменить его соответствующим
Popen
subprocess
, сделайте следующее:
Методика Заменен на
(child_stdin, p = Popen ('cmd', shell = True,
child_stdout_and_stderr) = bufsize = bufsize,
os.popen4 ('cmd', mode, bufsize) stdin = PIPE, stdout = PIPE, stderr
= STDOUT, close_fds = True)
(child_stdin,
child_stdout_and_stderr) =
(p.stdin, p.stdout)
Следующий код даст тот же результат, что и в предыдущих примерах, который показан в первом выводе кода выше.
import os
in, out = os.popen4('ls -la')
print(we.read())
Как видно из приведенного выше кода, метод очень похож на popen2
.
Однако out
файл в программе покажет объединенные результаты потоков
stdout и stderr.
Резюме различий
Все различия между различными popen*
связаны с их выводом, который
кратко изложен в таблице ниже:
Методика Аргументы
открывать стандартный вывод popen2 стандартный ввод, стандартный вывод popen3 стандартный ввод, стандартный вывод, стандартный поток popen4 stdin, stdout и stderr
Кроме того, popen2
, popen3
и popen4
доступны только в Python 2,
но не в Python 3. В Python 3 доступен popen
, но вместо него
рекомендуется использовать subprocess
, который мы опишем более
подробно в следующий раздел.
Метод susbprocess.Popen
Модуль subprocess
был создан с намерением заменить несколько методов, доступных в os
,
которые не считались очень эффективными. В этом модуле мы находим новый
класс Popen
Документация Python рекомендует использовать Popen
в сложных случаях,
когда другие методы, такие как subprocess.call
не могут удовлетворить
наши потребности. Этот метод позволяет выполнять программу как дочерний
процесс. Поскольку это выполняется операционной системой как отдельный
процесс, результаты зависят от платформы.
Доступны следующие параметры:
subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)
Одно из основных отличий Popen
заключается в том, что это класс, а не
просто метод. Таким образом, когда мы вызываем subprocess.Popen
, мы
фактически вызываем конструктор класса Popen
.
В конструкторе довольно много аргументов. Самым важным для понимания
является args
, который содержит команду для процесса, который мы
хотим запустить. Его можно указать как последовательность параметров
(через массив) или как одну командную строку.
Второй аргумент, который важно понять, - это shell
, значение которой
по умолчанию равно False
. В Unix, когда нам нужно запустить команду,
принадлежащую оболочке, например ls -la
, нам нужно установить
shell=True
.
Например, следующий код вызовет команду Unix ls -la
через оболочку.
import subprocess
subprocess.Popen('ls -la', shell=True)
Результаты можно увидеть в выводе ниже:
$ python subprocess_popen_test.py
total 40
drwxr-xr-x 7 scott staff 238 Nov 9 09:13 .
drwxr-xr-x 29 scott staff 986 Nov 9 09:08 ..
-rw-r--r-- 1 scott staff 52 Nov 9 09:13 popen2_test.py
-rw-r--r-- 1 scott staff 55 Nov 9 09:14 popen3_test.py
-rw-r--r-- 1 scott staff 53 Nov 9 09:14 popen4_test.py
-rw-r--r-- 1 scott staff 49 Nov 9 09:13 popen_test.py
-rw-r--r-- 1 scott staff 56 Nov 9 09:16 subprocess_popen_test.py
Используя следующий пример на компьютере с Windows, мы можем легче
shell
Здесь мы открываем Microsoft Excel из оболочки или как
исполняемую программу. Из оболочки это похоже на то, как если бы мы
открывали Excel из командного окна.
Следующий код откроет Excel из оболочки (обратите внимание, что мы
должны указать shell=True
):
import subprocess
subprocess.Popen("start excel", shell=True)
Однако мы можем получить те же результаты, вызвав исполняемый файл
Excel. В этом случае мы не используем оболочку, поэтому оставляем ее со
значением по умолчанию ( False
); но мы должны указать полный путь к
исполняемому файлу.
import subprocess
subprocess.Popen("C:\Program Files (x86)\Microsoft Office\Office15\excel.exe")
Кроме того, когда мы создаем экземпляр Popen
, у нас есть доступ к
нескольким полезным методам:
Методика Описание
Popen.poll()
Проверяет, завершен ли дочерний процесс.
Popen.wait()
Подождите, пока дочерний процесс завершится.
Popen.communicate()
Позволяет взаимодействовать с процессом.
Popen.send_signal()
Посылает сигнал дочернему процессу.
Popen.terminate()
Останавливает дочерний процесс.
Popen.kill()
Убивает дочерний процесс.
Полный список можно найти в документации
подпроцесса .
Наиболее часто используемый метод - это communicate
.
Метод communicate
позволяет нам считывать данные со стандартного
ввода, а также позволяет отправлять данные на стандартный вывод. Он
возвращает кортеж, определенный как (stdoutdata, stderrdata)
.
Например, следующий код объединяет команды Windows dir
и sort
.
import subprocess
p1 = subprocess.Popen('dir', shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen('sort /R', shell=True, stdin=p1.stdout)
p1.stdout.close()
out, err = p2.communicate()
Чтобы объединить обе команды, мы создаем два подпроцесса: один для
команды dir
а другой - для команды sort
. Поскольку мы хотим
выполнить сортировку в обратном порядке, мы добавляем параметр /R
к
вызову sort
Мы определяем стандартный вывод процесса 1 как PIPE, что позволяет нам
использовать вывод процесса 1 в качестве ввода для процесса 2. Затем нам
нужно закрыть стандартный вывод процесса 1, чтобы его можно было
использовать в качестве ввода для процесса 2. связь между процессами
достигается с помощью метода communicate
Запуск этого из командной оболочки Windows дает следующее:
> python subprocess_pipe_test.py
11/09/2017 08:52 PM 234 subprocess_pipe_test.py
11/09/2017 07:13 PM 99 subprocess_pipe_test2.py
11/09/2017 07:08 PM 66 subprocess_pipe_test3.py
11/09/2017 07:01 PM 56 subprocess_pipe_test4.py
11/09/2017 06:48 PM <DIR> ..
11/09/2017 06:48 PM <DIR> .
Volume Serial Number is 2E4E-56A3
Volume in drive D is ECA
Directory of D:\MyPopen
4 File(s) 455 bytes
2 Dir(s) 18,634,326,016 bytes free
Подведение итогов
В os
представляли собой хороший вариант, однако в настоящее время в
subprocess
есть несколько методов, которые являются более мощными и
эффективными в использовании. Среди доступных инструментов есть Popen
, который можно использовать в более сложных случаях. Этот класс также
содержит communicate
, который помогает нам объединять различные
команды для более сложных функций.
Для чего вы используете popen*
и какие предпочитаете? Дайте нам знать
об этом в комментариях!