Running external programs with Python (os.system and subprocess)
One of the things we often have to do is glue together various programs written by other people.
If these other programs are GUI based then we will have a very hard time doing so, but if they are command line based then there are some nice ways to do that in Python. We’ll see two different ways to accomplish this.
Our external program
In order to demonstrate this we need an «external tool» that we will handle as a «black box». As you are using Python I can assume you already have Python on your computer so we’ll use a script written in Python as the «external tool». You can see it here:
import time import sys if len(sys.argv) != 3: exit(f"sys.argv[0]> SECONDS EXIT_CODE") seconds = int(sys.argv[1]) exit_code = int(sys.argv[2]) for sec in range(seconds): print("OUT <>".format(sec), flush=True) print("ERR <>".format(sec), file=sys.stderr) time.sleep(1) exit(exit_code)
- Output on Standard Output (STDOUT)
- Output on Standard Error (STDERR)
- A process that takes time
- Various exit codes (ERRORLEVELs)
So we can see how to deal with either of those.
The user of the process.py can tell it how many iterations to do. On every iteration it will print to both STDOUT and STDERR and wait for 1 second.
The user can also tell the process how to set its exit code.
This can be a nice tool to fake the behavior of some external tool.
If we run the process as follows:
We get the following output:
OUT 0 ERR 0 OUT 1 ERR 1 OUT 2 ERR 2
We can also observe the exit code on Linux/macOS:
Using os.system
The simplest way to run an external program is to use os.system .
import os import sys exit_code = os.system(f"python process.py 5 2") print(f'exit code: exit_code // 256>')
It accepts a string — exactly what you would type in on the command line and executes the external program.
Your program connects its own STDOUT and STDERR channels to the external process so while the external program is running
whatever it prints to STDOUT and STDERR will be handled exactly as if they came directly from your program.
It waits till the external program ends and at the end it returns the exit-code of the external program.
Well it actually returns two bytes and the real exit code is in the higher byte so we need to do an integer division
exit_code // 256 in order to get to the real value. (It is the same as int(exit_code / 256) .)
OUT 0 ERR 0 OUT 1 ERR 1 OUT 2 ERR 2 OUT 3 ERR 3 OUT 4 ERR 4 exit code: 2
This can be very useful, but this way the output of the external program «gets lost» to our program. Often this is not what we want.
Often we would want to capture the output of the external program, parse it and do something based on the understanding from it.
We might also want to do something else while the external program does its thing.
Let’s see how the subprocess module can help us. We will see a few examples.
subprocess waiting for external process to finish
In the first example we will imitate the os.system just to lay the ground-work.
We have created a function called run_process . Instead of a string, the command we would want to type in,
it is expected to receive a list. The pieces of the command divided up. That is probably not be a problem to write.
I sprinkled the whole program with print statements to make it easier to see what is the order of things happening.
The first thing is to call proc = subprocess.Popen(command) . This will start the external program and return immediately
passing us an object that represents this external process. ( Popen stands for process open )
At this point the external program will run regardless of what our program does. So we can wait for 1.5 seconds and see the output (and error)
of the external program. We could also do some other work while the external program runs. We’ll see that later.
At one point, however, we will likely want to wait for the external program to end. This is what the proc.communicate() does.
(It’s name is strange, I know. The next example will shed some light on why it is called that way.)
It stops our program and waits till the external program ends.
Then we can fetch the exit code (that Windows calls ERRORLEVEL) from the attribute returncode of the proc object.
(Are you already having fun by the fact that the same thing is called «exit code», ERRORLEVEL, and «returncode» by three different systems?)
import subprocess import time def run_process(command): print("Before Popen") proc = subprocess.Popen(command) # This starts runing the external process print("After Popen") time.sleep(1.5) print("Before communicate") proc.communicate() print("After communicate") exit_code = proc.returncode return exit_code print("Before run_process", flush=True) exit_code = run_process(['python', 'process.py', '5', '0']) print("After run_process", flush=True) print(f'exit code: exit_code>', flush=True)
Запуск других программ из Python
Программа на Python может запускать другие программы с помощью функции Popen() (от process open) встроенного модуля subprocess. В качестве аргумента функция принимает имя программы, которую нужно запустить:
>>> import subprocess >>> subprocess.Popen(‘C:\\Windows\\System32\\calc.exe’)
Возвращаемое значение представляет собой объект Popen , имеющий два полезных метода: poll() и wait() .
Метод poll() возвращает значение None , если в момент его вызова процесс все еще выполняется. Если же процесс к этому моменту завершен, то он возвращает код завершения процесса. Код заверешения служит индикатором того, завершился ли процесс без ошибок (код равен нулю) или же его завершение было вызвано ошибкой (ненулевой код).
Метод wait() ожидает завершения запущенного процесса, прежде чем продолжить выполнение основной программы. Возвращаемым значением метода является целочисленный код завершения процесса.
>>> notepad = subprocess.Popen('C:\\Windows\\System32\\notepad.exe') >>> notepad.poll() == None True >>> notepad.poll() == None False >>> notepad.wait() 0 >>> notepad.poll() 0
Сначала мы открываем процесс блокнота, затем проверяем, возвращает ли метод poll() значение None . Это означает, что процесс все еще выполняется. После этого закрываем блокнот и еще раз проверяем, возвращает ли метод poll() значение None . Теперь оба метода, wait() и poll() возвращают нулевое значение, что указывает на завершение программы notepad.exe без ошибок.
Передача аргументов командной строки
Процессам, создаваемым с помощью функции Popen() , можно передвать аргументы командной строки. Для этого функции Popen() надо передать список в качестве единственного аргумента. Первой строкой в этом списке должно быть имя исполняемого файла программы, которую надо запустить. Все последующие строки — это аргументы командной строки, которые будут переданы программе при запуске.
>>> subprocess.Popen([‘C:\\Windows\\System32\\notepad.exe’, ‘C:\\example\\readme.txt’])
Здесь мы не только запускаем приложение notepad.exe , но и открываем файл readme.txt .
Открытие файлов программ по умолчанию
Двойной клик на иконке файла с расширением .txt позволяет автоматически запустить приложение, ассоциированное с этим расширением. Функция Popen() также может открывать файлы подобным образом:
>>> subprocess.Popen((‘start’, ‘C:\\example\\readme.txt’), shell = True)
В каждой операционной системе есть программа, выполняющая те же функции, что и двойной клик на иконке файла. В Windows это программа start , в Ubuntu Linux — программа see .
# Таймер обратного отсчета import time, subprocess wait = 10 while wait > 0: print(wait, end='') time.sleep(1) wait = wait - 1 # Воспроизведение звукового файла по завершении обратного отсчета subprocess.Popen(['start', 'C:\\example\alarm.wav'], shell = True)
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
- 1С:Предприятие (31)
- API (29)
- Bash (43)
- CLI (99)
- CMS (139)
- CSS (50)
- Frontend (75)
- HTML (66)
- JavaScript (150)
- Laravel (72)
- Linux (146)
- MySQL (76)
- PHP (125)
- React.js (66)
- SSH (27)
- Ubuntu (68)
- Web-разработка (509)
- WordPress (73)
- Yii2 (69)
- БазаДанных (95)
- Битрикс (66)
- Блог (29)
- Верстка (43)
- ИнтернетМагаз… (84)
- КаталогТоваров (87)
- Класс (30)
- Клиент (27)
- Ключ (28)
- Команда (68)
- Компонент (60)
- Конфигурация (62)
- Корзина (32)
- ЛокальнаяСеть (28)
- Модуль (34)
- Навигация (31)
- Настройка (140)
- ПанельУправле… (29)
- Плагин (33)
- Пользователь (26)
- Практика (99)
- Сервер (74)
- Событие (27)
- Теория (105)
- Установка (66)
- Файл (47)
- Форма (58)
- Фреймворк (192)
- Функция (36)
- ШаблонСайта (68)