如何从 Deno 运行任意 shell 命令?



我想从Deno运行任何任意bash命令,就像我在Node中使用child_process一样。这在德诺可能吗?

Deno 1.28.0 添加了一个新的 API 来运行 shell 命令:Deno.Command(需要--allow-run权限(

let cmd = new Deno.Command("echo", { args: ["hello world"] });
let { code, stdout, stderr } = await cmd.output();
// stdout & stderr are a Uint8Array
console.log(new TextDecoder().decode(stdout)); // hello world

更高级的用法:

const command = new Deno.Command(Deno.execPath(), {
args: [
"eval",
"console.log('Hello World')",
],
stdin: "piped",
stdout: "piped",
});
const child = command.spawn();
// open a file and pipe the subprocess output to it.
child.stdout.pipeTo(
Deno.openSync("output", { write: true, create: true }).writable,
);
// manually close stdin
child.stdin.close();
const status = await child.status;
const s = await c.status;
console.log(s);

此 API 替换了已弃用的Deno.run

<小时 />

旧答案:

为了运行 shell 命令,您必须使用Deno.run,这需要--allow-run权限。

有一个正在进行的讨论,即使用--allow-all来运行子流程


以下内容将输出到stdout

// --allow-run
const process = Deno.run({
cmd: ["echo", "hello world"]
});
// Close to release Deno's resources associated with the process.
// The process will continue to run after close(). To wait for it to
// finish `await process.status()` or `await process.output()`.
process.close();

如果要存储输出,则必须将stdout/stderr设置为"piped"

const process = Deno.run({
cmd: ["echo", "hello world"], 
stdout: "piped",
stderr: "piped"
});

const output = await process.output() // "piped" must be set
const outStr = new TextDecoder().decode(output);
/* 
const error = await p.stderrOutput();
const errorStr = new TextDecoder().decode(error); 
*/
process.close();

确保awaitstatusoutput使用Deno.run创建的子进程。

否则,进程可能会在执行任何代码之前被终止。例如:

deno run --allow-run main.ts
主要.ts:
const p = Deno.run({
cmd: ["deno", "run", "--allow-write", "child.ts"],
});
const { code } = await p.status(); // (*1); wait here for child to finish
p.close();
儿童网:
// If we don't wait at (*1), no file is written after 3 sec delay
setTimeout(async () => {
await Deno.writeTextFile("file.txt", "Some content here");
console.log("finished!");
}, 3000);

通过stdin/stdout传递参数:

主要.ts:
const p = Deno.run({
cmd: ["deno", "run", "--allow-write", "child.ts"],
// Enable pipe between processes
stdin: "piped",
stdout: "piped",
stderr: "piped",
});
if (!p.stdin) throw Error();
// pass input to child
await p.stdin.write(new TextEncoder().encode("foo"));
await p.stdin.close();
const { code } = await p.status();
if (code === 0) {
const rawOutput = await p.output();
await Deno.stdout.write(rawOutput); // could do some processing with output
} else { /* error */ }
儿童网:
import { readLines } from "https://deno.land/std/io/bufio.ts"; // convenient wrapper
// read given input argument
let args = "";
for await (const line of readLines(Deno.stdin)) {
args += line;
}
setTimeout(async () => {
await Deno.writeTextFile("file.txt", `Some content here with ${args}`);
console.log(`${args} finished!`); // prints "foo finished!""
}, 3000);

Deno 文档中也有一个很好的示例资源。

你可以像这样使用run来做到这一点:

// myscript.js
Deno.run({
cmd: ["echo", "hello world"]
})

运行脚本时必须--allow-run才能使其正常工作:

deno run --allow-run ./myscript.js

如果你的 shell 命令在进程即将结束之前打印出一些消息,你真的希望将 stdin 和 stdout 管道到你自己的流中,并且还会抛出一个你可以捕获的异常。

您甚至可以在将处理流管道到您自己的流时更改输出:

async function run(cwd, ...cmd) {
const stdout = []
const stderr = []
cwd = cwd || Deno.cwd()
const p = Deno.run({
cmd,
cwd,
stdout: "piped",
stderr: "piped"
})
console.debug(`$ ${cmd.join(" ")}`)
const decoder = new TextDecoder()

streams.readableStreamFromReader(p.stdout).pipeTo(new WritableStream({
write(chunk) {
for (const line of decoder.decode(chunk).split(/r?n/)) {
stdout.push(line)
console.info(`[ ${cmd[0]} ] ${line}`)
}
},
}))
streams.readableStreamFromReader(p.stderr).pipeTo(new WritableStream({
write(chunk) {
for (const line of decoder.decode(chunk).split(/r?n/)) {
stderr.push(line)
console.error(`[ ${cmd[0]} ] ${line}`)
}
},
}))
const status = await p.status()
if (!status.success) {
throw new Error(`[ ${cmd[0]} ] failed with exit code ${status.code}`)
}
return {
status,
stdout,
stderr,
}
}

如果每个可写流没有不同的逻辑,也可以将它们合并为一个:

streams.mergeReadableStreams(
streams.readableStreamFromReader(p.stdout),
streams.readableStreamFromReader(p.stderr),
).pipeTo(new WritableStream({
write(chunk): void {
for (const line of decoder.decode(chunk).split(/r?n/)) {
console.error(`[ ${cmd[0]} ] ${line}`)
}
},
}))

或者,您也可以通过任务运行器(如 drake(调用 shell 命令,如下所示

import { desc, run, task, sh } from "https://deno.land/x/drake@v1.5.0/mod.ts";
desc("Minimal Drake task");
task("hello", [], async function () {
console.log("Hello World!");
await sh("deno run --allow-env src/main.ts");
});
run();
$ deno run -A drakefile.ts hello

最新更新