跳到主要内容

9 篇博文 含有标签「node.js」

查看所有标签

Node.js 中 child_process 模块中的 exec 和 spawn

· 阅读需 4 分钟
1adybug
子虚伊人

Node.js 的 child_process 模块允许你从 Node.js 应用程序内部运行其他程序和命令。它是一个非常强大的功能,可以用来执行操作系统级别的任务,例如启动一个新的进程来处理 CPU 密集型工作或者运行系统命令。

child_process 模块是 Node.js 中一个强大的模块,它被用于各种场景,如:

  • 在后台运行定时任务 (比如,备份数据库)
  • 利用多核 CPU 优势来提高应用性能
  • 运行系统命令,进行文件操作
  • 实现基本的并行处理

运用这个模块可以帮助你的 Node.js 应用与操作系统更紧密地交互,从而执行一些复杂的任务。不过,也需要注意,错误地使用 child_process 模块可能会导致安全问题,比如如果用户输入没有得到恰当的处理,就可能会引发命令注入攻击。因此,在使用它时必须小心谨慎。

这里有几个主要的函数和类,你可以通过 child_process 模块使用:

exec

exec 用于执行一个命令并且将结果以回调函数的形式返回。它适合用于那些产生少量输出的情况

const { exec } = require("child_process")

exec("ls", (error, stdout, stderr) => {
if (error) {
console.error(`执行的错误: ${error}`)
return
}

console.log(`stdout: ${stdout}`)
console.error(`stderr: ${stderr}`)
})

在这个例子中,我们使用了 exec 函数来运行 ls 命令,这个命令会列出当前文件夹中的所有文件。如果执行成功,它的输出会被打印到控制台。

调用方法:exec(command[, options][, callback])

参数解释:

  1. command (必须): 你想要执行的命令字符串。
  2. options (可选): 一个对象,可以用来定制操作的各种设置,例如:
    • cwd:指定子进程的当前工作目录。
    • env:环境变量键值对。
    • encoding:输出的编码。
    • timeout:超时时间,过了这个时间子进程会被杀掉。
    • shell:要使用的 shell,如果不指定,默认在 UNIX 上是 /bin/sh,在 Windows 上是 cmd.exe
  3. callback (可选): 当进程终止或有错误发生时调用的回调函数,其参数包括:
    • error:错误对象或者 null
    • stdout:子进程的标准输出。
    • stderr:子进程的标准错误输出。

execFile

在计算机中,进程(Process)是正在运行的程序的实例。Node.js 提供了一个 child_process 模块,使得它可以从 Node.js 程序中创建和控制子进程。execFile 函数是这个模块中的一个非常有用的方法,用于创建一个新的子进程来执行一个指定的程序文件,并且可以传递参数给这个程序

调用方法:execFile(file[, args][, options][, callback])

参数解释:

  1. file (必须):这是你想要执行的可执行文件的名称或者路径。如果这个文件在系统的 PATH 环境变量里定义的目录中,你可以直接提供文件名;否则,你需要提供完整的路径。

  2. args (可选):这是一个可选参数,是一个数组,包含所有你想要传递给程序的命令行参数。

  3. options (可选):同 exec 中的 options

  4. callback (可选):同 exec 中的 callback

spawn

exec 相比,spawn 会返回一个流(Stream),这使得它更适用于需要处理大量数据的情况。

const { spawn } = require("child_process")

const child = spawn("find", ["."])

child.stdout.on("data", data => {
console.log(`stdout: ${data}`)
})

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

child.on("close", code => {
console.log(`子进程退出码:${code}`)
})

fork

这个函数是特别为 Node.js 模块设计的。它允许你创建一个 Node.js 进程,并运行一个模块。这对于在后台执行一个任务特别有用,而不必担心阻塞主事件循环。

const { fork } = require("child_process")

const child = fork("some-module.js")

child.on("message", message => {
console.log("收到消息:", message)
})

child.send({ hello: "world" })

在这个例子中,fork 创建了一个新的 Node.js 进程来运行 some-module.js 文件。父进程通过 .send() 方法发送消息给子进程,并通过监听 message 事件来接受子进程发送的消息。

...todo

在 Node.js 中为 fetch 配置代理

· 阅读需 1 分钟
1adybug
子虚伊人

Node.js 中,原生的 fetch API 并不直接支持代理功能。可以使用 node-fetch 库和 https-proxy-agent 库来实现通过代理服务器发送请求的功能:

import { HttpsProxyAgent } from "https-proxy-agent"
import fetch from "node-fetch"

// 代理服务器的URL
const proxyUrl = "http://your-proxy-server:port"

// 目标URL,即你想要通过代理服务器访问的URL
const targetUrl = "http://example.com"

// 配置代理服务器
const proxyAgent = new HttpsProxyAgent(proxyUrl)

// 使用fetch发起请求,通过代理服务器访问目标URL
fetch(targetUrl, { agent: proxyAgent })
注意

与原生的 fetch 不同,node-fetchresponse.json() 返回值类型为 Promise<unknown>

在 linux 中安装 nvm

· 阅读需 1 分钟
1adybug
子虚伊人

由于国内的网络环境限制,直接安装 nvm 脚本会失败:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

此时可以使用国内的 gitee 镜像源:https://gitee.com/mirrors/nvm

git clone https://gitee.com/mirrors/nvm
cd nvm
bash install.sh

打印长时间运行命令的输入

· 阅读需 3 分钟
1adybug
子虚伊人

如果你想要实时打印长时间运行命令的输出,比如监控某个过程的日志输出,你应该使用 child_process 中的 spawn 来代替 exec,因为 spawn 提供了一个流式接口,可以让你实时接收数据。下面是一个使用 spawn 实时打印输出的示例:

import { spawn } from "child_process"

const child = spawn("node -v", { shell: true })

child.stdout.on("data", data => {
console.log(`stdout: ${data}`)
})

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

child.on("close", code => {
console.log(`child process exited with code ${code}`)
})

这段代码会实时打印出命令的标准输出和标准错误输出。使用 spawn 时,你可以通过监听 data 事件来实时获取命令的输出。这对于需要实时监控输出的场景非常有用。

记得替换 your_long_running_command_here 为你想要执行的实际命令。

在命令行中执行的许多命令会使用特殊的 ANSI 转义序列来改变输出的字体颜色或样式,从而使输出更易于阅读。默认情况下,当这些命令通过 Node.jsspawn 函数执行时,它们可能检测到自己不是在一个交互式终端环境中运行,因此可能不会输出这些颜色或样式。

要使 spawn 打印的输出效果与直接在命令行中执行的效果一样(包括字体颜色),你可以尝试以下几种方法:

  1. 强制命令以颜色输出

    一些命令提供了参数或环境变量来强制启用颜色输出,即使输出被重定向。例如,很多命令支持 --color 选项:

    import { spawn } from "child_process"

    const child = spawn("some-command", ["--color", "always"])

    对于那些不支持此类选项的命令,可能需要查看该命令的文档,看是否有其他方法可以强制启用颜色输出。

  2. 使用 shell 选项

    通过在 spawn 中设置 shell: true 选项,可以在一个 shell 环境中执行命令。这样做可能会鼓励一些命令输出颜色,因为它们“认为”自己是在一个终端中运行:

    import { spawn } from "child_process"

    const child = spawn("some-command", ["arg1", "arg2"], { shell: true })
  3. 保持输出到进程的标准输出

    如果你只是想要在 Node.js 脚本中直接查看输出(包括颜色),而不是处理输出数据,你可以将子进程的 stdoutstderr 直接连接到主进程的 stdoutstderr

    import { spawn } from "child_process"

    const child = spawn("some-command", ["arg1", "arg2"], { stdio: "inherit" })

    使用 stdio: "inherit" 选项,子进程的输出会直接显示在终端中,包括所有的颜色和格式化。

    示例 假设你想要使用 spawn 执行 git status 命令,并希望输出包含颜色:

    import { spawn } from "child_process"

    // 注意:这里使用shell: true来确保命令在shell中执行,可能有助于保留颜色输出
    const child = spawn("git", ["status", "--color=always"], {
    shell: true,
    stdio: "inherit",
    })

    child.on("exit", function (code, signal) {
    console.log(`子进程退出,退出码 ${code}`)
    })

    这个例子中,--color=always 告诉 git status 命令总是使用颜色输出,shell: true 确保在一个 shell 环境中执行命令,stdio: "inherit" 使得命令的输出直接显示在终端中,包括颜色。

注意

使用 shell: true 可能会增加安全风险,特别是当命令的参数来自不可信的源时。因此,只有在确实需要时才使用这个选项,并确保对输入进行适当的清理和验证。

在 Express 中使用 https 证书

· 阅读需 1 分钟
1adybug
子虚伊人
import { readFileSync } from "fs"
import https from "https"

import express from "express"

const app = express()

app.get("/", async (req, res) => {
res.send("Hello, World!")
})

https
.createServer(
{
key: readFileSync("../root/.acme.sh/a.deep-sea.dynv6.net_ecc/a.deep-sea.dynv6.net.key"),
cert: readFileSync("../root/.acme.sh/a.deep-sea.dynv6.net_ecc/fullchain.cer"),
},
app,
)
.listen(8080)

ReadAbleStream 转换为 ReadStream

· 阅读需 1 分钟
1adybug
子虚伊人
import { Readable } from "stream"

const reader = readAbleStream.getReader()

const readStream = new Readable({
read() {
reader.read().then(({ done, value }) => {
if (done) readStream.push(null)
else readStream.push(value)
})
},
})

async function* nodeStreamToIterator(stream: ReadStream): AsyncGenerator<Buffer, void, never> {
for await (const chunk of stream) yield chunk
}

function iteratorToStream(iterator: AsyncGenerator<Buffer, void, never>): ReadableStream {
return new ReadableStream({
async pull(controller) {
const { value, done } = await iterator.next()

if (done) controller.close()
else controller.enqueue(value)
},
})
}
提示

2024 年 3 月 29 日更新

Node.js 中其实自带了转换的功能

import { Readable } from "stream"

async function main() {
const response = await fetch("http://example.com")
const readable = Readable.fromWeb(response.body as any)
}

main()