rusty-pee
A Rust port of the moreutils pee utility: fan a single stdin stream out to N concurrent shell-spawned children, aggregate their exit codes, and surface failures cleanly.
Install
# or, no toolchain required:
Prebuilt binaries ship for the five DDR-003 targets (Linux x86_64/aarch64, macOS x86_64/aarch64, Windows x86_64).
Usage
# Default mode — fan stdin out to N children
|
# Capture mode — emit child stdouts in argv order after they exit
|
# Strict mode — byte-equal moreutils stderr + bitwise-OR exit aggregation
# When installed via the `pee-alias` Cargo feature, the `pee` binary
# auto-activates Strict mode (argv[0] auto-detect).
Cargo Features
Five supported build configurations:
| Feature | Default | Includes |
|---|---|---|
cli |
✓ | clap, clap_complete, anyhow, signal-hook; the rusty-pee binary |
pee-alias |
✗ | also produces a second pee binary (auto-Strict via argv[0]) |
bench |
✗ | criterion harness in benches/throughput.rs |
dev-helpers |
✗ | gates the fake-pee-child test helper binary |
default-features = false |
n/a | library-only build; no clap/anyhow/clap_complete/signal-hook |
Library API
use ;
use Cursor;
let input = new;
let mut sink_a: = Vecnew;
let mut sink_b: = Vecnew;
let mut pee = new
.sink
.sink
.compat
.build?;
pee.run?;
assert_eq!;
assert_eq!;
# Ok::
The library fans out to N Box<dyn Write + Send> sinks instead of subprocesses — embed the fan-out pattern without spawning processes.
Compatibility statement (vs moreutils pee)
Pinned upstream version: moreutils 0.69 (same baseline as the rest of the rusty-* portfolio).
Pipeline-safety guarantee: every live sink receives every byte the parent reads, in argv (CLI) / registration (library) order. When a child closes its stdin mid-stream (BrokenPipe), the parent drops it from the live-set and continues feeding survivors. Memory is bounded — O(BUFSIZ × N), not O(input size).
Documented intentional divergences from moreutils pee:
- Default-mode exit-code aggregation uses
max(child_codes)instead of moreutils' bitwise OR overWEXITSTATUS. Strict mode preserves moreutils' bitwise-OR exactly. Example: children exiting 1 and 2 → Default exit=2, Strict exit=3. - Hardcoded platform shell:
/bin/sh -c(Unix) /cmd /C(Windows). No--shell <path>flag in v0.1.0. - Unknown-flag stderr format in Strict mode emits ONLY the first unknown-flag error (
rusty-pee: invalid option -- 'X'orrusty-pee: unknown option -- 'foo'). moreutils' POSIXGetopt::Longiterates per-character; we don't replicate that for undocumented inputs. --capture,--help,--version,completionssubcommand — not present in moreutils. Default-mode additions; rejected in Strict mode.--no-ignore-sigpipe/--no-ignore-write-errorsnegation flags — moreutils exposes these to opt OUT of the default-on behavior; rusty-pee v0.1.0 freezes the defaults on and provides no negation surface.- Configurable SIGTERM→SIGKILL grace period — v0.1.0 hardcodes 1 second (no flag, no env).
pee-alias PATH-collision warning. Building with --features pee-alias installs a second binary named pee alongside rusty-pee. If moreutils is also installed on the same PATH, whichever directory comes first wins. Invoke rusty-pee (always unambiguous) or omit the pee-alias feature when moreutils is also present.
See docs/COMPATIBILITY.md for the full per-flag matrix.
Stability (lockstep SemVer)
Library and binary share a single crate version. Within 0.x, minor version bumps may introduce breaking changes per standard Cargo semantics. Every public enum and struct is #[non_exhaustive] so variant additions are not breaking changes once 1.0 lands.
MSRV
Minimum supported Rust version: 1.85 (edition 2024 floor). This is an upward deviation from the portfolio's "current stable minus two" rule, forced by edition 2024.
License
Dual-licensed under either MIT or Apache-2.0 at your option.