我如何 运行 来自 Deno 的任意 shell 命令?
How do I run an arbitrary shell command from Deno?
我想 运行 来自 Deno 的任意 bash 命令,就像我在 Node.js 中使用 child_process
一样。这在 Deno 中可能吗?
您可以像这样使用 run
来做到这一点:
// myscript.js
Deno.run({
cmd: ["echo", "hello world"]
})
您必须 --allow-run
当 运行 脚本才能运行时:
deno run --allow-run ./myscript.js
为了运行一个shell命令,你必须使用Deno.run
,这需要--allow-run
权限。
有一个 ongoing discussion 可以使用 --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
or output
of the child process created with Deno.run
.
否则,进程可能会在执行任何代码之前被杀死。例如:
deno run --allow-run main.ts
main.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();
child.ts:
// 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
:
传递参数
main.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 */ }
child.ts:
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 docs 中也有很好的示例资源。
或者,您也可以通过任务运行程序调用 shell 命令,例如 drake,如下所示
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
如果您的 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}`)
}
},
}))
我想 运行 来自 Deno 的任意 bash 命令,就像我在 Node.js 中使用 child_process
一样。这在 Deno 中可能吗?
您可以像这样使用 run
来做到这一点:
// myscript.js
Deno.run({
cmd: ["echo", "hello world"]
})
您必须 --allow-run
当 运行 脚本才能运行时:
deno run --allow-run ./myscript.js
为了运行一个shell命令,你必须使用Deno.run
,这需要--allow-run
权限。
有一个 ongoing discussion 可以使用 --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
or output
of the child process created with Deno.run
.
否则,进程可能会在执行任何代码之前被杀死。例如:
deno run --allow-run main.ts
main.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();
child.ts:
// 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
:
传递参数
main.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 */ }
child.ts:
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 docs 中也有很好的示例资源。
或者,您也可以通过任务运行程序调用 shell 命令,例如 drake,如下所示
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
如果您的 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}`)
}
},
}))