Nodejs执行脚本遇到的问题

Nodejs执行脚本遇到的问题

具体是这样的,做了一个在网页执行后台脚本的东西,现在遇到的问题的是,后台脚本有些执行时间比较长,并且结果是一行行慢慢显示的,我现在想做的是在网页端也一行行显示???现在只实现了等待脚本执行完,后一次显示

我现在是用node.js实现的,执行脚本用的是chiild_process 中的exec,直接取其stdout当成结果,我要怎么一行一行通过exec去取结果哇

9 回复

当然可以!你提到的问题是在使用 Node.js 的 child_process 模块中的 exec 方法执行长时间运行的脚本时,希望能够在网页端实时地一行行显示脚本的输出。exec 方法并不适合这种场景,因为它会等到整个进程结束才会返回所有输出。

你可以考虑使用 spawnfork 方法来处理这种场景,因为它们可以更灵活地处理流数据。下面是具体的解决方案:

使用 spawn 方法

spawn 方法允许你更精细地处理输入输出流。你可以监听 stdout 流并逐行读取输出,然后将这些输出发送到前端。

示例代码

const { spawn } = require('child_process');
const express = require('express');

const app = express();
app.use(express.json());

app.post('/run-script', (req, res) => {
    const scriptPath = req.body.scriptPath; // 假设脚本路径从请求体中获取
    const scriptArgs = req.body.scriptArgs || []; // 脚本参数

    const child = spawn('node', [scriptPath, ...scriptArgs]);

    child.stdout.on('data', (data) => {
        const lines = data.toString().split('\n').filter(line => line.trim() !== '');
        lines.forEach(line => {
            console.log(`Received: ${line}`);
            // 这里可以将每一行的数据发送到前端
            // 例如,通过 WebSocket 或其他方式
        });
    });

    child.stderr.on('data', (data) => {
        console.error(`stderr: ${data}`);
    });

    child.on('close', (code) => {
        console.log(`子进程退出,退出码 ${code}`);
        res.status(200).send({ message: '脚本执行完成' });
    });
});

app.listen(3000, () => {
    console.log('服务器运行在 http://localhost:3000');
});

实现前端接收

假设你使用 WebSocket 来实现实时通信,你可以使用如下的客户端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>NodeJS Script Output</title>
</head>
<body>
    <div id="output"></div>
    <script>
        const socket = new WebSocket('ws://localhost:3000/ws');

        socket.onmessage = function(event) {
            const outputDiv = document.getElementById('output');
            const newLine = document.createElement('div');
            newLine.textContent = event.data;
            outputDiv.appendChild(newLine);
        };
    </script>
</body>
</html>

总结

通过使用 spawn 方法,你可以更灵活地处理长时间运行的脚本输出,并将其逐行传递给前端。这样,用户可以在网页上实时看到脚本的每一行输出。希望这能解决你的问题!


另外我现在是用scoket.io去监听是否有消息,进行推送的,在exec执行的过程中,只能一步拿到所有结果,不能一行行实时地拿…

设计这样一个机制: 1、客户端产生一个随机数ID。 2、客户端准备好批量数据的持续接收处理事件: socket.on( 随机数ID_data, function(data){}); socket.on( 随机数ID_end, function(data){}) … 3、客户端 emit( 后台命令, 参数, 随机数ID) 4、服务器端执行命令,持续把数据发回给客户端 socket.emit( 随机数ID_data, 数据 ) 5、执行完毕,服务器端发送 socket.emit( 随机数ID_end, 数据 )

楼上~你这想法确实好,受教了,但是还有一个问题我后台用child_process exec函数去执行shell脚本,我只能通过stdout一次拿到所有的结果,不能刚输出一行结果就让socket.emit发送尼

不知道这个能不能满足你的要求 procstreams

我也有这个问题。这个是child_process exec的局限。

不知道你脚本文件的内容是什么?

使用pty.js,启动一个pty(伪终端窗口),把脚本里面的命令一个一个逐次执行,在data事件中处理每个命令的输出信息。

大兄弟,你现在有解决方法了么?来自三年后的问候

在Node.js中使用child_process模块的exec方法来执行长时间运行的脚本时,默认情况下exec会等待整个命令执行完毕后返回结果。因此,你需要使用spawn方法而不是exec方法,因为spawn允许你实时获取输出。

示例代码

const { spawn } = require('child_process');

// 使用spawn来启动一个子进程
const child = spawn('your-script-command', ['arg1', 'arg2']);

// 监听子进程的标准输出
child.stdout.on('data', (data) => {
    console.log(`Received data: ${data}`);
    // 这里可以将data发送到前端,实现逐行显示
});

// 监听子进程的错误输出
child.stderr.on('data', (data) => {
    console.error(`Error: ${data}`);
});

// 子进程关闭时触发
child.on('close', (code) => {
    console.log(`Child process exited with code ${code}`);
});

解释

  • spawn方法:用于创建一个子进程,它与exec不同,不会等待命令完成后再返回结果。你可以立即从子进程中读取数据。
  • stdout.on('data', callback):当子进程产生标准输出时,回调函数会被调用。每次产生新数据时都会触发。
  • stderr.on('data', callback):如果子进程产生错误输出,可以通过这种方式捕获。
  • close事件:当子进程结束时触发。

如何将结果传递到前端

为了将结果传递到前端,你可以使用WebSocket、Server-Sent Events (SSE) 或轮询等方式实现实时通信。例如,如果你使用WebSocket,可以在Node.js服务器端接收到数据时向客户端发送更新:

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

child.stdout.on('data', (data) => {
    wss.clients.forEach((client) => {
        if (client.readyState === WebSocket.OPEN) {
            client.send(data.toString());
        }
    });
});

这样就可以实现实时更新,而无需等待脚本执行完毕。

回到顶部