#process #tokio #future #management #async #command #status

nightly bin+lib tokio-process

An implementation of an asynchronous process management backed futures

14 releases

0.3.0-alpha.2 Aug 19, 2019
0.2.5 Feb 5, 2020
0.2.4 Jun 22, 2019
0.2.3 Nov 2, 2018
0.1.0 Sep 10, 2016

#276 in #status

Download history 1156/week @ 2024-07-21 1139/week @ 2024-07-28 1001/week @ 2024-08-04 1227/week @ 2024-08-11 934/week @ 2024-08-18 1199/week @ 2024-08-25 1094/week @ 2024-09-01 1039/week @ 2024-09-08 856/week @ 2024-09-15 1162/week @ 2024-09-22 1271/week @ 2024-09-29 818/week @ 2024-10-06 1016/week @ 2024-10-13 1061/week @ 2024-10-20 1246/week @ 2024-10-27 941/week @ 2024-11-03

4,330 downloads per month
Used in fewer than 18 crates

MIT license

575KB
10K SLoC

tokio-process

An implementation of process management for Tokio

License

This project is licensed under the MIT license.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Tokio by you, shall be licensed as MIT, without any additional terms or conditions.


lib.rs:

An implementation of asynchronous process management for Tokio.

This crate provides a Command struct that imitates the interface of the std::process::Command type in the standard library, but provides asynchronous versions of functions that create processes. These functions (spawn, status, output and their variants) return "future aware" types that interoperate with Tokio. The asynchronous process support is provided through signal handling on Unix and system APIs on Windows.

Examples

Here's an example program which will spawn echo hello world and then wait for it complete.

#![feature(async_await)]

use tokio_process::Command;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // The usage is the same as with the standard library's `Command` type, however the value
    // returned from `spawn` is a `Result` containing a `Future`.
    let child = Command::new("echo").arg("hello").arg("world")
                        .spawn();

    // Make sure our child succeeded in spawning and process the result
    let future = child.expect("failed to spawn");

    // Await until the future (and the command) completes
    let status = future.await?;
    println!("the command exited with: {}", status);
    Ok(())
}

Next, let's take a look at an example where we not only spawn echo hello world but we also capture its output.

#![feature(async_await)]

use tokio_process::Command;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Like above, but use `output` which returns a future instead of
    // immediately returning the `Child`.
    let output = Command::new("echo").arg("hello").arg("world")
                        .output();

    let output = output.await?;

    assert!(output.status.success());
    assert_eq!(output.stdout, b"hello world\n");
    Ok(())
}

We can also read input line by line.

#![feature(async_await)]

use futures_util::stream::StreamExt;
use std::process::{Stdio};
use tokio::codec::{FramedRead, LinesCodec};
use tokio_process::Command;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut cmd = Command::new("cat");

    // Specify that we want the command's standard output piped back to us.
    // By default, standard input/output/error will be inherited from the
    // current process (for example, this means that standard input will
    // come from the keyboard and standard output/error will go directly to
    // the terminal if this process is invoked from the command line).
    cmd.stdout(Stdio::piped());

    let mut child = cmd.spawn()
        .expect("failed to spawn command");

    let stdout = child.stdout().take()
        .expect("child did not have a handle to stdout");

    let mut reader = FramedRead::new(stdout, LinesCodec::new());

    // Ensure the child process is spawned in the runtime so it can
    // make progress on its own while we await for any output.
    tokio::spawn(async {
        let status = child.await
            .expect("child process encountered an error");

        println!("child status was: {}", status);
    });

    while let Some(line) = reader.next().await {
        println!("Line: {}", line?);
    }

    Ok(())
}

Caveats

While similar to the standard library, this crate's Child type differs importantly in the behavior of drop. In the standard library, a child process will continue running after the instance of std::process::Child is dropped. In this crate, however, because tokio_process::Child is a future of the child's ExitStatus, a child process is terminated if tokio_process::Child is dropped. The behavior of the standard library can be regained with the Child::forget method.

Dependencies

~3.5MB
~60K SLoC