You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flask中通过subprocess实现命令实时输出到HTML页面?

在Flask中实现tail -f命令的实时输出

你的问题核心在于**communicate()方法的阻塞特性**——这个方法会一直等待子进程完全结束才返回输出,但tail -f是持续运行的命令,永远不会主动退出,所以你的代码会一直卡住,直到进程被强制终止,自然没法实现实时输出。

要实现实时推送命令输出,我们需要结合Flask的流式响应和前端的Server-Sent Events (SSE),让后端持续向浏览器发送新的输出内容。下面是具体的修改方案:

第一步:修正后端代码(app.py)

我们需要创建一个返回流式响应的路由,逐行读取tail -f的输出并推送给前端。同时注意你之前的命令参数写错了——tail -f要拆分成两个独立参数:

from subprocess import Popen, PIPE
from flask import Flask, render_template, Response

app = Flask(__name__)

@app.route("/")
def index():
    return render_template('index.html')

@app.route('/stream-logs')
def stream_logs():
    # 修正命令参数:tail和-f是两个独立的列表元素
    proc = Popen(['tail', '-f', '/var/log/syslog'], stdout=PIPE, stderr=PIPE, text=True)

    def generate():
        while True:
            # 逐行读取标准输出,避免阻塞整段内容
            line = proc.stdout.readline()
            if not line:
                break
            # 按照SSE格式封装数据,确保前端能正确解析
            yield f"data: {line}\n\n"
        # 读取错误输出(可选)
        err = proc.stderr.read()
        if err:
            yield f"data: [ERROR] {err}\n\n"

    # 设置响应头,指定为SSE类型,禁用缓存保持长连接
    return Response(generate(), mimetype='text/event-stream', headers={
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    })

@app.route('/page')
def fist():
    return render_template('page.html')

if __name__ == "__main__":
    # 注意:Debug模式下Flask会启动多进程,可能导致tail命令重复执行,建议关闭debug或添加use_reloader=False
    app.run(debug=False)

第二步:修改前端页面(page.html)

使用SSE的EventSource对象连接后端的流式接口,实时接收新的日志内容并追加到页面中:

<!DOCTYPE html>
<html>
<head>
    <style>
        .exit_data {
            overflow-y: scroll;
            height: 400px;
            background-color: #f5f5f5;
            padding: 10px;
            font-family: monospace;
            white-space: pre-wrap;
        }
    </style>
    <title>实时日志输出</title>
</head>
<body>
    <div>
        <h2>实时系统日志</h2>
    </div>
    <div class="exit_data" id="log-container"></div>

    <script>
        // 建立SSE连接到后端流式路由
        const eventSource = new EventSource('/stream-logs');
        
        // 监听后端推送的消息
        eventSource.onmessage = function(event) {
            const logContainer = document.getElementById('log-container');
            // 将新日志行追加到容器,保留原始格式
            logContainer.textContent += event.data;
            // 自动滚动到最新内容
            logContainer.scrollTop = logContainer.scrollHeight;
        };

        // 处理连接异常
        eventSource.onerror = function(error) {
            console.error('SSE连接出错:', error);
            eventSource.close();
        };
    </script>
</body>
</html>

关键注意事项

  • 命令参数格式:subprocess.Popen的命令必须是列表形式,每个空格分隔的参数都是列表的独立元素,所以tail -f要写成['tail', '-f'],而非['tail-f']。
  • SSE格式要求:后端返回的每条消息必须以data: 开头,以\n\n结尾,这是EventSource能正确解析的标准格式。
  • Flask Debug模式:开启Debug时Flask会启动两个进程,可能导致tail -f被重复执行,建议关闭Debug或添加use_reloader=False参数。
  • 资源清理:当用户关闭页面时,SSE连接会自动断开,后端的子进程也会随之终止,无需额外手动清理。

内容的提问来源于stack exchange,提问作者Ricardo Oliveira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 00:07:49