我想从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();
确保await
status
或output
使用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