我正在尝试将shell脚本移植到可读性更强的python版本。原来的shell脚本使用“&”在后台启动几个进程(实用程序、监视器等)。如何在python中实现相同的效果?我希望这些进程在python脚本完成时不死亡。我确信这与守护进程的概念有关,但我不知道如何轻松地做到这一点。
当前回答
两者都捕获输出并通过线程在后台运行
正如在这个答案中提到的,如果使用stdout=捕获输出,然后尝试read(),那么进程将阻塞。
然而,在某些情况下您需要这样做。例如,我想启动两个通过端口进行通信的进程,并将它们的标准输出保存到日志文件和标准输出中。
threading模块允许我们这样做。
首先,看看如何在这个问题中单独执行输出重定向部分:Python Popen:同时写入标准输出和日志文件
然后:
main.py
#!/usr/bin/env python3
import os
import subprocess
import sys
import threading
def output_reader(proc, file):
while True:
byte = proc.stdout.read(1)
if byte:
sys.stdout.buffer.write(byte)
sys.stdout.flush()
file.buffer.write(byte)
else:
break
with subprocess.Popen(['./sleep.py', '0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc1, \
subprocess.Popen(['./sleep.py', '10'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc2, \
open('log1.log', 'w') as file1, \
open('log2.log', 'w') as file2:
t1 = threading.Thread(target=output_reader, args=(proc1, file1))
t2 = threading.Thread(target=output_reader, args=(proc2, file2))
t1.start()
t2.start()
t1.join()
t2.join()
sleep.py
#!/usr/bin/env python3
import sys
import time
for i in range(4):
print(i + int(sys.argv[1]))
sys.stdout.flush()
time.sleep(0.5)
在运行:
./main.py
Stdout每0.5秒更新一次,每两行包含:
0
10
1
11
2
12
3
13
每个日志文件都包含给定进程的日志。
灵感来源:https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/
在Ubuntu 18.04, Python 3.6.7上测试。
其他回答
虽然jkp的解决方案是可行的,但较新的做事方式(以及文档推荐的方式)是使用子流程模块。对于简单的命令,它是等效的,但是如果您想执行一些复杂的操作,它提供了更多的选项。
举个例子:
import subprocess
subprocess.Popen(["rm","-r","some.file"])
这将运行rm -r some。文件在后台。注意,在Popen返回的对象上调用. communication()将阻塞直到它完成,所以如果你想让它在后台运行,就不要这样做:
import subprocess
ls_output=subprocess.Popen(["sleep", "30"])
ls_output.communicate() # Will block for 30 seconds
请在这里查看文档。
另外,需要澄清的一点是:你在这里使用的“Background”纯粹是一个shell概念;从技术上讲,您的意思是希望在等待进程完成时不阻塞地生成进程。但是,我在这里使用的“background”指的是类似shell-background的行为。
你可以使用
import os
pid = os.fork()
if pid == 0:
Continue to other code ...
这将使python进程在后台运行。
我还没有尝试过这个,但使用.pyw文件而不是.py文件应该有帮助。Pyw文件没有控制台,所以理论上它不应该像后台进程一样出现和工作。
两者都捕获输出并通过线程在后台运行
正如在这个答案中提到的,如果使用stdout=捕获输出,然后尝试read(),那么进程将阻塞。
然而,在某些情况下您需要这样做。例如,我想启动两个通过端口进行通信的进程,并将它们的标准输出保存到日志文件和标准输出中。
threading模块允许我们这样做。
首先,看看如何在这个问题中单独执行输出重定向部分:Python Popen:同时写入标准输出和日志文件
然后:
main.py
#!/usr/bin/env python3
import os
import subprocess
import sys
import threading
def output_reader(proc, file):
while True:
byte = proc.stdout.read(1)
if byte:
sys.stdout.buffer.write(byte)
sys.stdout.flush()
file.buffer.write(byte)
else:
break
with subprocess.Popen(['./sleep.py', '0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc1, \
subprocess.Popen(['./sleep.py', '10'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc2, \
open('log1.log', 'w') as file1, \
open('log2.log', 'w') as file2:
t1 = threading.Thread(target=output_reader, args=(proc1, file1))
t2 = threading.Thread(target=output_reader, args=(proc2, file2))
t1.start()
t2.start()
t1.join()
t2.join()
sleep.py
#!/usr/bin/env python3
import sys
import time
for i in range(4):
print(i + int(sys.argv[1]))
sys.stdout.flush()
time.sleep(0.5)
在运行:
./main.py
Stdout每0.5秒更新一次,每两行包含:
0
10
1
11
2
12
3
13
每个日志文件都包含给定进程的日志。
灵感来源:https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/
在Ubuntu 18.04, Python 3.6.7上测试。
你可能想知道“如何在Python中调用外部命令”的答案。
最简单的方法是使用操作系统。系统功能,例如:
import os
os.system("some_command &")
基本上,无论您传递给系统函数的是什么,都将像在脚本中传递给shell一样执行。
推荐文章
- 如何在Python中进行热编码?
- 如何嵌入HTML到IPython输出?
- 在Python生成器上使用“send”函数的目的是什么?
- 是否可以将已编译的.pyc文件反编译为.py文件?
- Django模型表单对象的自动创建日期
- 在Python中包装长行
- 如何计算两个时间串之间的时间间隔
- 我如何才能找到一个Python函数的参数的数量?
- 您可以使用生成器函数来做什么?
- 将Python诗歌与Docker集成
- 提取和保存视频帧
- 使用请求包时出现SSL InsecurePlatform错误
- 如何检索Pandas数据帧中的列数?
- except:和except的区别:
- 错误:“字典更新序列元素#0的长度为1;2是必需的”