Qubit Command
Command-line process running utilities for Rust.
Overview
Qubit Command provides a small, structured API for running external programs, capturing their output, enforcing timeouts, and reporting command failures with clear error values.
Features
- Structured command execution with program and argument vectors
- Explicit shell command support for cases that require shell parsing
- Configurable timeout, working directory, stdin, environment variables, and success exit codes
- Process-tree termination on timeout using Unix process groups and Windows Job Objects
- UTF-8 stdout and stderr text accessors, with raw byte accessors for binary output
- Optional per-stream capture limits plus streaming tee files for large output
- Sanitized command diagnostics for sensitive argv values, explicit environment overrides, shell payloads, and caller-defined sensitive fields
- Typed errors for spawn failures, timeouts, failed output reads, and unexpected exit codes
Timeout Behavior
CommandRunner::new() does not enforce a timeout by default. Use
timeout(Duration) when a command must be bounded, or without_timeout() when
the absence of a timeout should be explicit in builder chains.
When a timeout is configured, the runner attempts to terminate the process tree: Unix commands are spawned in a new process group and Windows commands are spawned in a Job Object.
Large Output
By default stdout and stderr are captured without an in-memory byte limit. For commands that can emit large logs, configure capture limits and tee files:
use ;
let output = new
.max_output_bytes
.tee_stdout_to_file
.tee_stderr_to_file
.run?;
if output.stdout_truncated
# Ok::
Quick Start
use Duration;
use ;
let output = new
.timeout
.run?;
println!;
# Ok::
Shell Commands
Prefer structured commands whenever possible:
use ;
let output = new
.run?;
assert_eq!;
# Ok::
Use Command::shell only when shell parsing, redirection, expansion, or
pipes are intentional:
use ;
let output = new
.run?;
assert_eq!;
# Ok::
Sanitized Diagnostics
Command strings used in runner logs, CommandError::command(), and
Command's Debug output are sanitized with qubit-sanitize.
Sensitive structured argv values such as --password secret,
--access-token=..., and OPENAI_API_KEY=... are masked. Explicit
environment overrides are shown only in sanitized form. Command::shell
payloads are treated as opaque shell scripts and displayed as
<shell command> instead of being parsed.
Add application-specific fields on the runner when the defaults are not enough:
use ;
let error = new
.sensitive_field
.run
.expect_err;
assert_eq!;
Runner-specific fields affect runner logs and CommandError::command().
Standalone Command Debug output has no runner context and uses the built-in
defaults only.
Captured stdout/stderr bytes and tee files remain raw process output. Use capture limits and caller-side filtering when command output itself may contain secrets.
Output Text
stdout() and stderr() return raw bytes exactly as retained. Use
stdout_text() and stderr_text() when the command output must be valid UTF-8.
Use stdout_lossy_text() and stderr_lossy_text() to replace invalid UTF-8
bytes with �.
If the captured stdout or stderr contains invalid UTF-8, stdout_text() /
stderr_text() return Err(str::Utf8Error) from str::from_utf8. The bytes are
still stored on the returned CommandOutput; use stdout() / stderr() to read
the raw output and decode or handle it yourself.
use ;
let output = new
.run?;
assert_eq!;
# Ok::
Testing
A minimal local run:
To mirror what continuous integration enforces, run the repository scripts from the project root: ./align-ci.sh brings local tooling and configuration in line with CI, then ./ci-check.sh runs the same checks the pipeline uses. For test coverage, use ./coverage.sh to generate or open reports (see the script’s help and any project coverage notes for options such as HTML or JSON).
Contributing
Issues and pull requests are welcome.
- Open an issue for bug reports, design questions, or larger feature proposals when it helps align on direction.
- Keep pull requests scoped to one behavior change, fix, or documentation update when practical.
- Before submitting, run
./align-ci.shand then./ci-check.shso your branch matches CI rules and passes the same checks as the pipeline. When you need to review or improve coverage, use./coverage.shas described under Testing. - Add or update tests when you change runtime behavior, and update this README (or public rustdoc) when user-visible API behavior changes.
By contributing, you agree to license your contributions under the Apache License, Version 2.0, the same license as this project.
License
Copyright © 2026 Haixing Hu, Qubit Co. Ltd.
This project is licensed under the Apache License, Version 2.0. See the LICENSE file in the repository for the full text.
Author
Haixing Hu — Qubit Co. Ltd.
| Repository | github.com/qubit-ltd/rs-command |
| Documentation | docs.rs/qubit-command |
| Crate | crates.io/crates/qubit-command |