windows-cli-tools
Native Windows .exe implementations of Unix CLI commands (head, tail, cat, grep, wc, tac, tee, touch, which, yes) with zero runtime dependencies. Drop them on PATH and they work in cmd, PowerShell, and any terminal.
Overview
- Language: Rust
- Repo: peteretelej/windows-cli-tools
- Install: Download from Releases or
cargo install --git https://github.com/peteretelej/windows-cli-tools head tail wc cat tac grep tee touch which yes - Status: active (10 tools)
Architecture
A Cargo workspace with one shared library crate and one binary crate per tool.
Workspace layout (crates/):
common- Shared library with three modules:encoding.rs- UTF-16 BOM detection and transcoding viaencoding_rs_io. Providesopen_input(),open_stdin(), andopen_input_or_stdin()that wrap file/stdin readers with automatic BOM sniffing. This is critical for Windows because PowerShell writes UTF-16LE with BOM by defaulterror.rs-err()(fatal, exits with code 1) andwarn()(non-fatal stderr). Two functions, no error typescli.rs-print_help()andprint_version()one-liners
head,tail,cat,tac,wc,grep,tee,touch,which,yes- One binary crate each
Dependency map: Most tools depend only on common + lexopt. The exception is grep, which adds regex-lite for pattern matching. Some simpler tools (tee, touch, which, yes) skip common entirely and use only lexopt directly. The workspace has exactly three external runtime dependencies: lexopt (arg parsing), encoding_rs_io (BOM handling), and regex-lite (grep only).
Tool pattern: Each tool follows the same structure:
- A
parse_args()function usinglexopt(manual pattern matching onShort/Long/Valuevariants) - A
run()function that returnsio::Result<()> - A
main()that callsrun()and handles broken pipe errors - Unit tests at the bottom of the file
The head crate is a good example: it defines a Mode enum (Lines/Bytes), parses args, opens input via encoding::open_input() or encoding::open_stdin(), and writes to locked stdout.
Key Design Decisions
lexopt over clap: Argument parsing uses lexopt, a zero-dependency parser that adds ~15KB to binaries vs clap’s ~300KB+. Each tool hand-matches on short/long flags, which is more code but produces tiny executables. This is the right trade-off for simple tools where the arg surface is small.
Zero runtime dependencies: The compiled .exe files link the C runtime statically (+crt-static), so there is nothing to install. No MSVC redistributable, no DLLs. This is what makes them practical for dropping onto any Windows machine, including CI runners and fresh installs.
regex-lite instead of regex: grep uses regex-lite, which trades some regex features and performance for a much smaller binary size (no Unicode tables or DFA compilation). For a CLI grep on typical inputs, this is the right trade-off.
UTF-16 BOM handling: Instead of requiring users to deal with encoding, every file-reading tool transparently detects UTF-16LE/BE BOM and transcodes to UTF-8. This is done once in common/encoding.rs and shared by all tools that read files.
Windows 7 support via Rust 1.75: The 32-bit builds target i686-pc-windows-msvc using Rust 1.75 (the last version supporting Windows 7), giving these tools reach on older systems that still need CLI utilities.
Workspace-level lint config: clippy::all = warn is set at the workspace level, so all crates share the same lint standard without per-crate configuration.
Development
cargo build --release --workspace
cargo test --workspace
cargo fmt --check
cargo clippy --workspace
# Windows 7 / 32-bit build (PowerShell)
$env:RUSTFLAGS = "-C target-feature=+crt-static"
cargo +1.75.0 build --release --workspace --target i686-pc-windows-msvc