Under heavy development. This site describes the intended 1.0 language. Current features and performance vary. Development status
Sn50SnThe SnAPI programming language
Development · Evolving reference

RunProcessWithStreams

Runs an explicit command with borrowed reader/writer stream adapters. Feeds stdin while draining stdout/stderr; selected adapters override standard stream inheritance. Handles short writes without implicit flush or allocation per chunk. Original IoError survives with its channel and partial prefix.

Sn is under heavy development. This reference includes the intended 1.0 design; features may be incomplete in the current compiler. Read the status →

Module std.process

import std.process;

Public declaration contracts and authored examples. A page can document several related symbols. Implementation bodies are excluded.

RunProcess

pub fn RunProcess<I, O, E>(Command: readonly ProcessCommand&, Input: mut I&, Output: mut O&, Errors: mut E&, Limit: Timeout, Token: CancellationToken) -> Result<ProcessExit, ProcessStreamError>
    where I: Reader where O: Writer where E: Writer

Runs an explicit command with borrowed reader/writer stream adapters. Feeds stdin while draining stdout/stderr; selected adapters override standard stream inheritance. Handles short writes without implicit flush or allocation per chunk. Original IoError survives with its channel and partial prefix. I: Concrete mutable input Reader. O: Concrete mutable output Writer. E: Concrete mutable stderr Writer. Command: Explicit command backing, borrowed unmodified through return. Input: Exclusive stdin source; zero successful read means EOF. Output: Exclusive stdout sink; prior delivered bytes survive failure. Errors: Exclusive stderr sink, independent of stdout. Limit: Native execution allowance; callback bodies cannot be preempted. Token: Owned cooperative cancellation observation. Return: Detached child outcome or exact native/channel failure after cleanup. Warning: May allocate/spawn/block. Adapters must return promptly for responsive cancellation and must not wait for another channel in this same operation. No stream view or callback backing escapes; caller retains stream ownership.

Example

module example.process_streams;
from std.process import Arguments, Environment, ProgramInputs, ProcessCommand, RunProcess, ProcessError, ProcessStreamError;
from std.fs import Path;
from std.text import NativeString;
from std.prelude import Result;
from std.io import IoError;
from std.time import Timeout, Seconds;
struct InputStream {
    pub Count: usize;
    pub init() { this.Count = 0; }
    pub fn Read(Output: mut byte[]) -> Result<usize, IoError> {
        if this.Count == 3 || Output.Length == 0 { return .Ok { Value = 0 }; }
        Output[0] = (0x61_u8 + (this.Count as u8)) as byte;
        this.Count += 1; return .Ok { Value = 1 };
    }
}
struct OutputStream {
    pub Bytes: byte[8]; pub Count: usize; pub Fail: bool;
    pub init() { this.Bytes = default(byte[8]); this.Count = 0; this.Fail = false; }
    pub fn Write(Input: readonly byte[]) -> Result<usize, IoError> {
        if Input.Length == 0 { return .Ok { Value = 0 }; }
        if this.Fail && this.Count == 1 { return .Err { Error = .Failure { Reason = .Interrupted, Transferred = 0 } }; }
        if this.Count == 8 { return .Err { Error = .OutputFailed }; }
        this.Bytes[this.Count] = Input[0]; this.Count += 1; return .Ok { Value = 1 };
    }
    pub fn Flush() -> Result<void, IoError> { return .Ok; }
}
fn CreateCommand() -> Result<ProcessCommand, ProcessError> {
    var Executable = {
        // Scoped writable carriers end immediately after explicit payload transfer.
        var ExecutableCreation = Path.TryCreate("/bin/sh");
        switch ExecutableCreation {
            case .Ok { Value = Value: mut Path& } => break move(Value);
            case .Err => return .Err { Error = ProcessError { Reason = .OutOfMemory, NativeCode = 0 } };
        }
    };
    var Directory = {
        var DirectoryCreation = Path.TryCreate("/");
        switch DirectoryCreation {
            case .Ok { Value = Value: mut Path& } => break move(Value);
            case .Err => return .Err { Error = ProcessError { Reason = .OutOfMemory, NativeCode = 0 } };
        }
    };
    var Values = Arguments();
    let CommandArguments: str[3] = ["/bin/sh", "-c", "printf err >&2; /bin/cat; exit 7"];
    for Text in CommandArguments {
        var Argument = {
            var ValueCreation = NativeString.TryCreate(Text);
            switch ValueCreation {
                case .Ok { Value = Value: mut NativeString& } => break move(Value);
                case .Err => return .Err { Error = ProcessError { Reason = .OutOfMemory, NativeCode = 0 } };
            }
        };
        if Values.TryPush(move(Argument)) is .Err { Error = _ } { return .Err { Error = ProcessError { Reason = .OutOfMemory, NativeCode = 0 } }; }
    }
    return .Ok { Value = ProcessCommand(move(Executable), move(Directory), ProgramInputs(move(Values), Environment())) };
}
fn Main() -> i32 {
    // Explicit Linux shell/cat prerequisites; no ambient environment is copied.
    var Command = {
        var CommandCreation = CreateCommand();
        switch CommandCreation {
            case .Ok { Value = Value: mut ProcessCommand& } => break move(Value);
            case .Err => return 1;
        }
    };
    let Limit = switch Timeout.TryFromDuration(Seconds.Create(2)) { case .Ok { Value = Value } => break Value; case .Err => return 2; };
    var Input = InputStream(); var Output = OutputStream(); var Errors = OutputStream();
    switch RunProcess(Command, Input, Output, Errors, Limit) {
        case .Ok { Value = PatternPayload5138 } => {
            switch PatternPayload5138 {
                case .Exited { Code = PatternPayload5139 } if (PatternPayload5139 == 7) => {}
                default => return 3;
            }
        }
        default => return 3;
    }
    if Input.Count != 3 || Output.Count != 3 || Errors.Count != 3 { return 4; }
    if Output.Bytes[0] != 0x61_byte || Output.Bytes[2] != 0x63_byte || Errors.Bytes[0] != 0x65_byte { return 5; }
    Input.Count = 0; Output.Count = 0; Output.Fail = true; Errors.Count = 0;
    switch RunProcess(Command, Input, Output, Errors, Limit) {
        case .Err { Error = PatternPayload5140 } => {
            switch PatternPayload5140 {
                case .Output { Error = PatternPayload5141 } => {
                    switch PatternPayload5141 {
                        case .Failure { Reason = PatternPayload5142, Transferred = Count } if (PatternPayload5142 is .Interrupted) => {
                            if Count > 1 || Output.Count != 1 { return 6; }
                        }
                        default => return 7;
                    }
                }
                default => return 7;
            }
        }
        default => return 7;
    }
    return 0;
}

RunProcess

pub fn RunProcess<I, O, E>(Command: readonly ProcessCommand&, Input: mut I&, Output: mut O&, Errors: mut E&) -> Result<ProcessExit, ProcessStreamError>
    where I: Reader where O: Writer where E: Writer

Supplies the omitted arguments to RunProcess. Note: Defaults: Limit = .Endless, Token = CancellationToken(). I: Same generic argument and constraints as the full operation. O: Same generic argument and constraints as the full operation. E: Same generic argument and constraints as the full operation. Command: Same input, ownership and lifetime contract as the full operation. Input: Same input, ownership and lifetime contract as the full operation. Output: Same input, ownership and lifetime contract as the full operation. Errors: Same input, ownership and lifetime contract as the full operation. Return: The full synchronous operation's value or typed failure.

RunProcess

pub fn RunProcess<I, O, E>(Command: readonly ProcessCommand&, Input: mut I&, Output: mut O&, Errors: mut E&, mut Limit: Timeout) -> Result<ProcessExit, ProcessStreamError>
    where I: Reader where O: Writer where E: Writer

Supplies the omitted arguments to RunProcess. Note: Defaults: Token = CancellationToken(). I: Same generic argument and constraints as the full operation. O: Same generic argument and constraints as the full operation. E: Same generic argument and constraints as the full operation. Command: Same input, ownership and lifetime contract as the full operation. Input: Same input, ownership and lifetime contract as the full operation. Output: Same input, ownership and lifetime contract as the full operation. Errors: Same input, ownership and lifetime contract as the full operation. Limit: Same input, ownership and lifetime contract as the full operation. Return: The full synchronous operation's value or typed failure.

Documentation snapshot: October 24, 2026 About versions ↗
Searching Development · language & standard library