Ubuntu22下Python3.10中subprocess.communicate()未等待进程完成问题
问题原因与解决方案
核心原因
ipsec up的默认行为是启动后立刻fork出后台守护进程,主进程会快速退出。而subprocess的communicate()、wait()、run()这些方法都是等主进程结束就返回,这时候VPN连接的初始化输出还没完全写入stdout/stderr,自然拿不到内容。加time.sleep(3)能拿到结果,是因为这段时间后台进程完成了初始化并输出了内容。
解决方案
1. 让ipsec在前台运行(优先选这个)
大部分IPSec实现(比如StrongSwan)的ipsec up支持--nofork参数,强制进程在前台跑,不后台化。这样subprocess就能等整个连接流程走完,正常捕获输出:
import subprocess connect_command = [ 'ip', 'netns', 'exec', 'vpn_test_namespace', 'ipsec', 'up', '--nofork', 'vpn_test_config' ] # 用run()更简洁,自动等进程完成 result = subprocess.run( connect_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True # 把输出转成字符串,方便处理 ) connect_output = result.stdout connect_error = result.stderr # 可以通过返回码判断连接是否成功 print(f"返回码: {result.returncode}") print(f"标准输出:\n{connect_output}") print(f"错误输出:\n{connect_error}")
2. 轮询VPN状态(如果--nofork用不了)
要是你的IPSec版本不支持--nofork,可以通过反复执行ipsec status命令来判断连接是否完成,之后再去拿日志:
import subprocess import time # 先启动连接 connect_command = [ 'ip', 'netns', 'exec', 'vpn_test_namespace', 'ipsec', 'up', 'vpn_test_config' ] subprocess.run(connect_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 最多等30秒,每隔1秒查一次状态 max_wait = 30 wait_interval = 1 elapsed = 0 connected = False while elapsed < max_wait: status_command = [ 'ip', 'netns', 'exec', 'vpn_test_namespace', 'ipsec', 'status', 'vpn_test_config' ] result = subprocess.run( status_command, stdout=subprocess.PIPE, text=True ) # 这里的关键字根据你的IPSec实际状态输出调整,比如'ESTABLISHED' if 'ESTABLISHED' in result.stdout: connected = True break time.sleep(wait_interval) elapsed += wait_interval # IPsec日志一般存在/var/log/ipsec.log,具体路径看系统配置 if connected: with open('/var/log/ipsec.log', 'r') as f: vpn_logs = f.read() print("VPN连接成功,日志内容:\n", vpn_logs) else: print("超时,没连上VPN")
3. 处理输出缓冲(辅助排查)
如果存在输出缓冲导致内容没及时写入的情况,可以禁用缓冲或者用unbuffer工具(需要先装expect包:sudo apt install expect):
import subprocess connect_command = [ 'ip', 'netns', 'exec', 'vpn_test_namespace', 'unbuffer', 'ipsec', 'up', 'vpn_test_config' ] result = subprocess.run( connect_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=0 # 禁用缓冲 ) print(result.stdout)
内容的提问来源于stack exchange,提问作者Serg
相关产品推荐
相关产品推荐

