使用Python的Django框架,后台调用bat或shell脚本,如何实时将输出内容展示到前端页面?

我很菜, 我现在能做的就是:

  • 用 python 把 bat 或 shell 脚本调用时输出的内容全部写一个 txt 里边
  • 然后再读文件,读取了展示到前端 页面中来,

问题是:

  • 但调用 bat 或 shell 脚本的时间过长,页面一直在处于空闲等待中,
  • 而且效率也太低了,请教下各位大佬,如何将调用脚本的输出,实时输出到页面上?
  • 调用脚本用的 os.system("test.bat"),交互模式下,持续有内容输出的

使用Python的Django框架,后台调用bat或shell脚本,如何实时将输出内容展示到前端页面?

18 回复

目测 subprocess.Popen 比较适合


核心方案: 使用Django Channels配合异步视图,通过WebSocket将脚本的实时输出推送到前端。

具体实现:

  1. 安装依赖:
pip install channels channels-daphne
  1. 配置Channels:
# settings.py
INSTALLED_APPS = [
    'channels',
    'django.contrib.staticfiles',
    # ...其他应用
]

ASGI_APPLICATION = 'your_project.asgi.application'

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels.layers.InMemoryChannelLayer',
    }
}
  1. 创建消费者(处理WebSocket连接):
# consumers.py
import asyncio
import subprocess
from channels.generic.websocket import AsyncWebsocketConsumer

class ScriptConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        await self.accept()
    
    async def receive(self, text_data):
        # 接收前端传来的脚本命令
        if text_data.startswith('run:'):
            script_cmd = text_data[4:]
            await self.run_script(script_cmd)
    
    async def run_script(self, cmd):
        # 创建子进程执行脚本
        process = await asyncio.create_subprocess_shell(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT
        )
        
        # 实时读取输出并发送到前端
        while True:
            line = await process.stdout.readline()
            if not line:
                break
            await self.send(text_data=line.decode('utf-8'))
        
        await process.wait()
  1. 路由配置:
# routing.py
from django.urls import path
from . import consumers

websocket_urlpatterns = [
    path('ws/script/', consumers.ScriptConsumer.as_asgi()),
]
  1. 前端页面:
<!-- script_runner.html -->
<div id="output"></div>
<script>
const socket = new WebSocket('ws://' + window.location.host + '/ws/script/');
const outputDiv = document.getElementById('output');

socket.onmessage = function(e) {
    outputDiv.innerHTML += e.data + '<br>';
    outputDiv.scrollTop = outputDiv.scrollHeight;
};

// 发送要执行的脚本命令
function runScript(cmd) {
    socket.send('run:' + cmd);
}
</script>

一句话总结: 用Channels的WebSocket实现实时数据流。

  • 后台开个新线程跑 subprocess.Popen(),用异步模式,然后不断把输出写入到一个临时文件中。
    - 前台用 Ajax,或者糙一点儿,用 meta 标签的定时刷新,每隔 N 读出那个临时文件的内容。

用关键字 python webshell 搜,有你想要的

做过类似的,我的解决办法是前端写 JS 调用后台接口后刷新页面.

之前弄了一个超简单的。。。
在 csdn 上找到的
声明一个全局 n
处理数据的函数每完成一项就把 n 加上某个数

js 设置定时器,每五秒向另一个 url 请求数据,url 对应的 views 就返回 n 的值

有个方案是用 Django 的 Channels (本质上是实现 websocket )实现信息的实时刷新
但是以你这个水平还是不建议自己搞。
直接 fork 下面这个开源项目吧
https://github.com/jumpserver/jumpserver

你就把
配备了业界领先的 Web Terminal 解决方案
这部分复制了就行了

同上,用 channels,本质上是 websocket。django 不断把 bat,shell 脚本输出的内容输送到 channel 里面,浏览器就可以实时读取了

目测你遇到的问题是请求被后台长时间运行的进程给 block 了,所以也可以使用异步队列( celery )。然后前端使用 ajax 轮询

有一个很简单的方法, 用 websocket, js 有可库可以一条命令把一个文件变成 ws 流, 这样就是实现了在前端展示文本变化的功能.

没用过 djano,只用过 Django。。。。

参考下:
subprocess.Popen(commandline, shell=True, stdout=subprocess.PIPE, cwd=workingPath).stdout.read()

😭😭😭😭😭😭😭😭😭😭

既然用 Django 了,用 Channels 就能很好解决了,h5 端用 ws 监听就可以了

回到顶部