Оглавление
2. Как передать аргументы в программу в subprocess.run
3. Получение результата выполнения команды
4. Работа со стандартным потоком ошибок
5. Автоматическая разбивка команды на список аргументов
6. Как запустить команду с подстановочными знаками (wildcard)
7. Вызов команды без разбития на список аргументов. Выполнение команды в оболочке. Опция shell=True
9. Как из скрипта Python запустить другой скрипт Python
10. Как завершить запущенное приложение по таймауту
11. subprocess.Popen — продвинутый вариант subprocess.run
12. Ошибки при использовании subprocess.run
12.1 Ошибка «NameError: name 'subprocess' is not defined. Did you forget to import 'subprocess'?»
12.2 Ошибка «FileNotFoundError: [Errno 2] No such file or directory»
12.3 Ошибка «subprocess.TimeoutExpired: Command»
Модуль subprocess в Python
Модуль subprocess позволяет создавать новые процессы. С помощью subprocess можно, например, выполнять любые команды Linux или Windows из скрипта. Также с помощью Python можно запустить другой скрипт Python.
С помощью subprocess возможно:
- запустить программу операционной системы или другой скрипт Python
- передать любое количество аргументов запускаемой программе
- подключаться к стандартным потокам ввода/вывода/ошибок и получать код возврата
- получать вывод запущенной команды
- проверить, что команда выполнилась без ошибок
Модуль subprocess входит в стандартные пакеты Python и не требует установки.
Функция subprocess.run — основной способ работы с модулем subprocess.
Самый простой вариант использования функции — запуск её таким образом:
import subprocess result = subprocess.run('ls')
Результат выполнения команды выводится в стандартный вывод — это можно изменить, ниже показано как это сделать.
В переменной result теперь содержится специальный объект CompletedProcess. Из этого объекта можно получить информацию о выполнении процесса, например, о коде возврата:
print(result.returncode)
Код 0 означает, что программа выполнилась успешно.
Как передать аргументы в программу в subprocess.run
Если вы попытаетесь указать какие-либо аргументы к запускаемой программе, то вы столкнётесь с ошибкой. Например, далее показан НЕ правильный вариант передачи аргументов:
import subprocess result = subprocess.run('ls -ls')
При выполнении этого скрипта возникнет ошибка «FileNotFoundError: [Errno 2] No such file or directory».
Если необходимо вызвать команду с аргументами, её нужно передавать таким образом (как список):
import subprocess result = subprocess.run(['ls', '-ls'])
Получение результата выполнения команды
По умолчанию функция run возвращает результат выполнения команды на стандартный поток вывода.
Если вы хотите поменять это поведение по умолчанию, а именно сохранить вывод команды в переменную вместо немедленного вывода на экран, то нужно добавить аргумент stdout и указать ему значение subprocess.PIPE.
Поскольку в данном случае будет возвращена не строка, а поток байтов, необходимо декодировать его. Это можно сделать с помощью отдельной команды, но удобнее указать опцию encoding='utf-8', для автоматического декодирования байтов в строку:
import subprocess result = subprocess.run(['ls', '-ls'], stdout=subprocess.PIPE, encoding='utf-8') print(result.stdout)
Если вы не указали опцию encoding='utf-8', то для декодирования полученных байтов нужно выполнить decode:
import subprocess result = subprocess.run(['ls', '-ls'], stdout=subprocess.PIPE) print(result.stdout.decode('utf-8'))
Работа со стандартным потоком ошибок
Если команда была выполнена с ошибкой или не отработала корректно, вывод команды попадёт на стандартный поток ошибок.
Получить этот вывод можно так же, как и стандартный поток вывода:
import subprocess result = subprocess.run(['ping', '-c', '3', '-n', 'a'], stderr=subprocess.PIPE, encoding='utf-8') print(result.stdout) print(result.stderr) print(result.returncode)
Будет выведено следующее:
None ping: a: Name or service not known 2
В result.stdout (то, что должна вывести программа) теперь только пустая строка (None); в result.stderr находится стандартный поток вывода ошибок; а в result.returncode помещён код ошибки
Автоматическая разбивка команды на список аргументов
Думаю, вручную разбивать готовую строку команды на аргументы — не самое интересное занятие. К тому же, если вы привыкли к удобному запуску команд в других языках программирования, когда можно указать команду и её аргументы в одну строку, то ручная разбивка команды на аргументы для subprocess.run кажется лишней работой. Более того, бывают довольно трудные случаи, когда не всегда очевидно, как правильно нужно разбить строку команды на аргументы, например:
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
Вы можете воспользоваться автоматической разбивкой команды с аргументами на список, который нужно передать в subprocess.run. Пример:
import shlex, subprocess command_line = 'ls -ls' args = shlex.split(command_line) result = subprocess.run(args)
В этом скрипте полная команда, вместе с аргументами, сохраняется в переменной command_line. Затем shlex.split разбивает команду на аргументы и сохраняет полученный список в переменную args. Затем этот список передаётся в subprocess.run для запуска команды.
Как запустить команду с подстановочными знаками (wildcard)
При попытке выполнить команду с использованием wildcard-выражений, например, при использовании *:
import subprocess result = subprocess.run(['ls', '-ls', '*php'])
Возникнет ошибка:
ls: cannot access '*php': No such file or directory
Чтобы вызывать команды, в которых используются wildcard-выражения, нужно добавлять аргумент shell=True и вызывать команду таким образом:
import subprocess result = subprocess.run('ls -ls *php', shell=True)
Вызов команды без разбития на список аргументов. Выполнение команды в оболочке. Опция shell=True
Вы могли обратить внимание, что предыдущую команду не пришлось разбивать на аргументы — она целиком передана в subprocess.run. Это стало возможным благодаря опции shell=True, которая означает, что указанная команда должна быть выполнена в оболочке.
То есть вы можете использовать опцию shell=True и вам больше не придётся разбивать команду на список аргументов:
import subprocess result = subprocess.run('ls -ls', shell=True)
Смотрите также: https://docs.python.org/3/library/subprocess.html#security-considerations
Как сделать так, чтобы subprocess.run не дожидалась завершения выполнения команды. Перевод запущенного процесса в фон
Ещё одна особенность функции run() — она ожидает завершения выполнения команды. Если попробовать, например, запустить команду ping, то этот аспект будет заметен:
import subprocess result = subprocess.run(['ping', '-c', '3', '-n', '8.8.8.8'])
Вы можете перевести запущенный процесс в фон. Для этого вам нужно:
1) использовать аргумент shell=True
2) записать строку команды без разбивки на аргументы
3) в конце команды добавить через пробел символ «&»
Например:
import subprocess result = subprocess.run('ping -c 3 -n 8.8.8.8 &', shell=True) print('It has been run!')
В данном случае надпись «It has been run!» будет выведена до запуска команды ping.
Как из скрипта Python запустить другой скрипт Python
В целом запуск программы Python из другой программы Python похож на процесс запуска программы ОС.
Пример:
import subprocess result = subprocess.run(['python', 't37.py'])
В данном случае будет запущен скрипт Python, который находится в файле t37.py — этот файл находится в той же директории, что и запускающая его программа, иначе необходимо указать полный путь, например:
import subprocess result = subprocess.run(['python', '/home/mial/test/p/t37.py'])
И ещё один пример с опцией shell=True:
import subprocess result = subprocess.run('python t37.py', shell=True)
Если вы соблюли условия, позволяющие вам запускать скрипт непосредственно в оболочке без явного указания интерпретатора (для этого файл должен быть исполнимым, и в начале файла должен быть указан шебанг), то вы можете запустить скрипт Python из другой программы Python следующим образом:
import subprocess result = subprocess.run('/home/mial/test/p/t37.py')
И ещё один вариант с опцией shell=True:
import subprocess result = subprocess.run('/home/mial/test/p/t37.py', shell=True)
Как завершить запущенное приложение по таймауту
С помощью опции timeout вы можете указать время выполнения запущенной программы, после которого приложение будет завершено принудительно и будет выведена ошибка. Время указывается в секундах. Пример:
import subprocess result = subprocess.run('ping -c 3 -n 8.8.8.8', shell=True, timeout=1)
Вы можете перехватывать сообщения об ошибке с помощью конструкции try…except:
import subprocess try: result = subprocess.run('ping -c 1 -n 8.8.8.7', shell=True, timeout=1) except (subprocess.TimeoutExpired): print("Time is over")
subprocess.Popen — продвинутый вариант subprocess.run
Вы также можете использовать subprocess.Popen, где создание и управление базовым процессом в этом модуле осуществляется классом Popen. Он обеспечивает большую гибкость, так что разработчики могут обрабатывать менее распространённые случаи, не охваченные функциями subprocess.run.
Например, с помощью subprocess.Popen вы можете запускать программы от имени другого пользователя.
Смотрите также: https://docs.python.org/3/library/subprocess.html#subprocess.Popen
Ошибки при использовании subprocess.run
Ошибка «NameError: name 'subprocess' is not defined. Did you forget to import 'subprocess'?»
Полный текст ошибки:
Traceback (most recent call last): File "/home/mial/test/p/t38.py", line 2, in <module> result = subprocess.run('ls -ls') ^^^^^^^^^^ NameError: name 'subprocess' is not defined. Did you forget to import 'subprocess'?
Эта ошибка означает, что вы не выполнили импорт модуля subprocess. То есть в начале вашего скрипта вам необходимо добавить строку:
import subprocess
Ошибка «FileNotFoundError: [Errno 2] No such file or directory»
Полный текст ошибки:
Traceback (most recent call last): File "/home/mial/test/p/t38.py", line 2, in <module> result = subprocess.run('ls -ls') ^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 548, in run with Popen(*popenargs, **kwargs) as process: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 1026, in __init__ self._execute_child(args, executable, preexec_fn, close_fds, File "/usr/lib/python3.12/subprocess.py", line 1955, in _execute_child raise child_exception_type(errno_num, err_msg, err_filename) FileNotFoundError: [Errno 2] No such file or directory: 'ls -ls'
Данная ошибка может возникнуть если вы вместо списка аргументов передаёте одну строку команды. В качестве альтернативы списку аргументов, вы можете использовать опцию «shell=True».
Ошибка «subprocess.TimeoutExpired: Command»
Полный текст ошибки:
Traceback (most recent call last): File "/home/mial/test/p/t38.py", line 2, in <module> result = subprocess.run('ping -c 3 -n 8.8.8.8', shell=True, timeout=1) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 550, in run stdout, stderr = process.communicate(input, timeout=timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 1209, in communicate stdout, stderr = self._communicate(input, endtime, timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 2141, in _communicate self.wait(timeout=self._remaining_time(endtime)) File "/usr/lib/python3.12/subprocess.py", line 1264, in wait return self._wait(timeout=timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 2045, in _wait raise TimeoutExpired(self.args, timeout) subprocess.TimeoutExpired: Command 'ping -c 3 -n 8.8.8.8' timed out after 0.9999772470036987 seconds
Данная ошибка возникает при истечении таймпута, то есть времени, выделенного на работу запускаемой программы. Если вам необходимо использовать таймаути, но вы не хотите, чтобы ваш скрипт завершал работу из-за данной ошибке, используйте конструкцию try…except.
Связанные статьи:
- Как добавить префикс «b» к имени переменной в Python? (100%)
- Решение проблемы со сломавшимся после обновления пакетов Pip (82.2%)
- Решение проблемы с ошибкой fatal error: libxml/xmlversion.h: Нет такого файла или каталога (82.2%)
- Решение проблемы с ошибкой ImportError: No module named requests (82.2%)
- Решение проблемы с ошибкой No module named pkg_resources (82.2%)
- Решение проблемы с ошибкой make: x86_64-w64-mingw32-gcc: Команда не найдена (RANDOM - 50%)