This is a pre-release. The API is stabilizing. The documentation is under construction. The project is usable.
deno-proc
An easy way to run processes like a shell script in Deno.
documentation
deno doc -q https://deno.land/x/proc/mod.ts
Examples
Key Concepts
Leaking Resources
One of the challenges of working with processes in Deno is that you must manually close every process resource - readers, writers, and the process itself.
There are other complications as well. You can’t close a resource more than
once, so you can’t have optional/defensive calls to close things. If you fail to
close a child process before you exit with Deno.exit()
, the child process will
live on - but child processes are automatically closed if you exit with
CTRL-c
.
This isn’t just complicated to use. It is really difficult to write correct code with the Deno process runner.
To solve these resource-management problems, we can run processes with a
Group
. A Group
is a Deno.Closer
, and when you close it, you tidy up all
the resources and processes associated with the group. Group
also makes sure
things get cleaned up properly when the Deno process exits.
Proc requires that all processes it manages are associated with a Group
.
const pg = group();
try {
console.log(
await runner(emptyInput(), stringOutput()).run(pg, {
cmd: ["ls", "-la"],
}),
);
} finally {
pg.close();
}
Input and Output Types
Raw stdin
, stdout
, and stderr
from a process are streamed byte data. This
is a simple definition, but it is not very useful. We need to be able to
interpret data differently in different circumstances.
Streamed byte data is the fastest, so if we are just piping bytes from one
process to another, we would use BytesIterableOutput()
for stdout
of process
#1 and BytesIterableInput()
for stdin
of process #2.
If you have a small amount of data (it can be kept in memory), StringInput()
and StringOutput()
let you work with string
data. For text data that is too
big to fit in memory, or if you just want to work with real-time streamed text
data, use StringIterableInput()
and StringIterableOutput()
. There is some
overhead associated with processing streamed bytes into text lines, but this is
how you will interact with process input and output much of the time.
An Example
This example shows how runner(...)
is used to generate a process definition.
In this case, I am going to pass in a string
and get back a Uint8Array
.
gzip
is just getting a stream of bytes in both cases of course. Our definition
is translating for us.
/**
* Use `gzip` to compress some text.
* @param text The text to compress.
* @return The text compressed into bytes.
*/
async function gzip(text: string): Promise<Uint8Array> {
const pg = group();
try {
/* I am using a string for input and a Uint8Array (bytes) for output. */
const processDef: Runner<string, Uint8Array> = runner(
stringInput(),
bytesOutput(),
);
return await processDef.run(pg, {
cmd: ["gzip", "-c"],
}, text);
} finally {
pg.close();
}
}
const pg = group();
try {
console.dir(await gzip("Hello, world."));
} finally {
pg.close();
}
Input Types
Name | Description |
---|---|
EmptyInput() |
There is no process input. |
StringInput() |
Process input is a string . |
BytesInput() |
Process input is a Uint8Array . |
ReaderInput() * |
Process input is a Deno.Reader & Deno.Closer . |
StringIterableInput() |
Process input is an AsyncIterable<string> . |
BytesIterableInput() |
Process input is an AsyncIterable<Uint8Array> . |
* - ReaderInput
is a special input type that does not have a
corresponding output type. It is not useful for piping data from process to
process.
Output Types
Name | Description |
---|---|
StringOutput() |
Process output is a string . |
BytesOutput() |
Process output is a Uint8Array . |
StringIterableOutput() |
Process output is an AsyncIterable<string> . |
BytesIterableOutput() |
Process output is an AsyncIterable<Uint8Array> . |
StderrToStdoutStringIterableOutput() * |
stdout and stderr are converted to text lines (string ) and multiplexed together. |
* - Special output type that mixes stdout
and stderr
together.
stdout
must be text data.
Examples
Run an Inline Bash Script
Starting with something simple yet useful, this is an example of running a
bash
script using runner
.
const pg = group();
try {
console.log(
await runner(emptyInput(), stringOutput()).run(pg, {
cmd: [
"/bin/bash",
"--login",
"-c",
"echo 'Hello, Deno.'",
],
}),
);
} finally {
pg.close();
}