scripty
scripty
Scripty - A simple and intuitive library that makes running shell commands and file operations easy and visible.
Why scripty?
When you need to write system administration scripts, build tools, or automation in Rust, you often
find yourself wrestling with std::process::Command
and std::fs
. scripty provides a clean,
shell-script-like interface while keeping all the benefits of Rust's type safety and error handling.
Key Features
- 🎨 Colorful output: See exactly what commands are being executed
- 🔗 Easy piping: Chain commands together naturally with stdout, stderr, or both
- 📁 File operations: Wrapper around
std::fs
with automatic logging - 🔧 Builder pattern: Fluent API for command construction
- ⚡ Minimal dependencies: Only uses
anstyle
for colors - 🛡️ Type safe: All the safety of Rust with the convenience of shell scripts
- 🚰 Streaming I/O: Efficient handling of large data with readers and writers
- 🔌 Reader Extensions: Fluent piping from any
Read
implementation to commands - ✍️ Write Methods: Direct output streaming to writers with stdout/stderr control
Quick Start
Add this to your Cargo.toml
:
[]
= "0.3.3"
Platform Support
Currently supported platforms:
- Linux ✅ Full support with native pipe optimization
- macOS ✅ Full support with native pipe optimization
Scripty is designed for Unix-like systems and uses Unix shell commands and utilities.
Requirements
- Rust 1.87.0 or later - Uses native
std::io::pipe
for optimal pipeline performance
Security Considerations
⚠️ Warning: This library executes system commands with the privileges of the current process.
- Never pass untrusted user input directly to commands
- Validate and sanitize all inputs before use
- Be aware of command injection risks when constructing command arguments dynamically
- Consider using allowlists for command names and arguments when dealing with user input
Example of unsafe usage:
// DON'T DO THIS with untrusted input!
let user_input = get_user_input;
// cmd!("sh", "-c", user_input).run()?; // Dangerous!
Basic Usage
Command Execution
use *;
// Simple command execution
cmd!.run?;
// Get command output
let output = cmd!.output?;
println!;
// Command with multiple arguments
cmd!.run?;
// Using the builder pattern
cmd!
.arg
.current_dir
.env
.run?;
Reader-to-Command Piping
Pipe data directly from any Read
implementation to commands using the ReadExt
trait:
use *;
use File;
use ;
// Pipe file contents directly to commands
let file = open?;
let result = file.pipe
.pipe
.pipe
.output?;
// Memory-efficient processing with BufReader
let large_file = open?;
let reader = new;
reader.pipe
.run?;
// In-memory data processing
let data = new;
let sorted = data.pipe.output?;
Command Piping
Chain commands together just like in shell scripts using native std::io::pipe
for enhanced
performance and memory efficiency!
use *;
// Simple pipe (stdout)
cmd!
.pipe
.run?;
// Pipe stderr
cmd!
.pipe_err
.run?;
// Pipe both stdout and stderr
cmd!
.pipe_out_err
.run?;
// Multiple pipes using efficient native pipes
cmd!
.pipe
.pipe
.run?;
// Get piped output with efficient streaming
let result = cmd!
.pipe
.pipe
.output?;
println!;
Pipeline Performance Features
- Memory efficient: Uses streaming instead of buffering all data
- Better performance: Native pipes reduce process overhead
- Platform independent: No shell dependency for multi-command pipes
- Native implementation: Uses
std::io::pipe
for optimal performance
use *;
// Large data processing with efficient streaming
let large_data = "..."; // Megabytes of data
let result = cmd!
.pipe
.pipe
.input
.output?; // Processes without loading all data into memory
Core API Reference
The cmd!
Macro
The heart of scripty is the cmd!
macro for creating commands:
use *;
// Basic command
cmd!.run?;
// Command with arguments
cmd!.run?;
// Multiple arguments
cmd!.run?;
Command Builder Methods
Commands support a fluent builder pattern:
use *;
cmd!
.arg // Add single argument
.args // Add multiple arguments
.current_dir // Set working directory
.env // Set environment variable
.no_echo // Suppress command echoing
.run?;
Execution Methods
Different ways to execute commands:
use *;
// Execute and check exit status
cmd!.run?;
// Capture text output
let output = cmd!.output?;
println!;
// Capture binary output
let bytes = cmd!.output_bytes?;
Output Streaming with Write Methods
Stream command output directly to writers with precise control over stdout/stderr:
use *;
use File;
// Stream stdout to a file
let output_file = create?;
cmd!.write_to?;
// Stream stderr to an error log
let error_file = create?;
cmd!.write_err_to?;
// Stream both stdout and stderr to the same destination
let combined_file = create?;
cmd!.write_both_to?;
// Use with any Writer (Vec, File, Cursor, etc.)
let mut buffer = Vec new;
cmd!.write_to?;
Input Methods
Provide input to commands in various ways:
use *;
use Cursor;
// Text input
let result = cmd!
.input
.output?;
println!;
// Binary input
let bytes = cmd!
.input_bytes
.output_bytes?;
// Stream from reader using ReadExt
use File;
let file = open?;
file.pipe.run?;
// Buffered reading for large files
use BufReader;
let large_file = open?;
let reader = new;
reader.pipe.run?;
Advanced I/O Control with spawn_io_* Methods
For complex I/O scenarios, use the spawn methods for non-blocking control:
use *;
use ;
use thread;
// Full I/O control with spawn_io_all
let spawn = cmd!.spawn_io_all?;
// Handle input in separate thread
if let Some = spawn.stdin
// Read output
if let Some = spawn.stdout
spawn.handle.wait?;
spawn_io_* Method Variants
Choose the right spawn method for your needs:
use *;
use Write;
// spawn_io_in - Control stdin only (common for sending data)
let = cmd!.spawn_io_in?;
if let Some = stdin
handle.wait?;
// spawn_io_out - Control stdout only (common for reading output)
let = cmd!.spawn_io_out?;
if let Some = stdout
handle.wait?;
// spawn_io_in_out - Control both stdin and stdout (interactive commands)
let = cmd!.spawn_io_in_out?;
// Send numbers to sort
if let Some = stdin
// Read sorted output
if let Some = stdout
handle.wait?;
// spawn_io_err - Control stderr only (error monitoring)
let = cmd!.spawn_io_err?;
if let Some = stderr
handle.wait?;
Simple Reader-to-Writer Operations
For straightforward input-to-output scenarios:
use *;
use File;
use Cursor;
// Process file data through command (stdout)
let input_file = open?;
let output_file = create?;
cmd!.run_with_io?;
// Capture error output while processing
let source_code = new;
let mut error_log = Vec new;
let _ = cmd!.run_with_err_io;
println!;
// Capture both stdout and stderr for comprehensive logging
let input_data = new;
let log_file = create?;
cmd!.run_with_both_io?;
File System Operations
All file operations are automatically logged:
use *;
// Basic file operations
write?;
let content = read_to_string?;
println!;
// Directory operations
create_dir_all?;
copy?;
// Directory traversal
for entry in read_dir?
// Cleanup
remove_file?;
remove_dir_all?;
Error Handling
Use standard Rust error handling patterns:
use *;
// Handle command failures gracefully
match cmd!.run
// Check command availability
if cmd!.no_echo.run.is_ok
// Use the ? operator for early returns
Global Configuration
Control scripty's behavior with environment variables:
NO_ECHO
: Set to any value to suppress command echoing globally
NO_ECHO=1
Or use the .no_echo()
method on individual commands.
Examples
This crate includes focused examples showcasing scripty's core strengths: pipeline operations and I/O handling:
Examples are numbered for optimal learning progression:
01_simple_pipes.rs
- Basic pipeline operations and command chaining02_pipe_modes.rs
- Complete pipeline control with stdout/stderr piping03_read_ext.rs
- Fluent reader-to-command piping with ReadExt trait04_run_with_io.rs
- Blocking reader-writer I/O with run_with_*() methods05_spawn_io.rs
- Non-blocking I/O control with spawn_io_*() methods
Run examples in order for the best learning experience:
Learning Path: Start with 01_simple_pipes.rs
and progress through each numbered example in
sequence to build your expertise with scripty's pipeline and I/O capabilities.
Real-World Example: cargo-xtask + clap + scripty
This project's xtask/
demonstrates scripty with cargo-xtask and clap:
See xtask/src/main.rs
for the complete implementation combining all three tools.
Advanced Pipeline Performance & Best Practices
Performance Optimization
scripty's native pipeline implementation provides significant performance benefits:
use *;
// ✅ Efficient: Native pipes with streaming
let result = cmd!
.pipe
.pipe
.pipe
.output?; // Processes without loading all data into memory
// ✅ Memory efficient: Stream large data directly
use File;
let large_file = open?;
large_file.pipe
.pipe
.output?; // Handles gigabytes efficiently
Pipeline Best Practices
Memory Management:
use *;
// ✅ Good: Stream processing for large data
cmd!
.pipe
.pipe
.output?;
// ❌ Avoid: Loading large outputs into memory first
// let large_output = cmd!("find", "/", "-type", "f").output()?; // Don't do this
// Instead, use streaming with pipes directly
cmd!
.pipe
.output?;
Error-Prone Pipelines:
use *;
// ✅ Good: Graceful error handling in pipelines
match cmd!
.pipe
.no_echo
.output
Complex Data Processing:
use *;
// ✅ Efficient multi-stage processing
let processed = cmd!
.pipe // Extract items
.pipe // Filter active
.pipe // Extract names
.pipe // Sort results
.output?;
Troubleshooting Common Issues
Large Data Processing:
use *;
// Problem: Memory usage with large files
// Solution: Use streaming with BufReader and ReadExt
use BufReader;
use File;
let large_file = open?;
let reader = new;
reader.pipe
.output?; // Processes efficiently regardless of file size
Pipeline Debugging:
use *;
// Enable command echoing for debugging (set before running your program)
// export SCRIPTY_DEBUG=1
// Commands are echoed by default unless .no_echo() is used
cmd!
.pipe
.pipe
.run?; // Commands will be shown as they execute
Error Isolation:
use *;
// Test each stage of a complex pipeline individually
let stage1 = cmd!.output?;
println!;
let stage2 = cmd!.input.output?;
println!;
// Then combine when each stage works correctly
Platform Support
- Linux ✅ Full support with native pipe optimization
- macOS ✅ Full support with native pipe optimization
- Windows ❌ Not supported (Unix-like systems only)
Contributing
We welcome contributions! Please see our GitHub repository for more information.
License
This project is licensed under the MIT License.
License: MIT