Both Coreutils Shipped This Week: GNU 9.12 Gets Faster, Rust 0.12 Closes Compatibility Gaps

Both Coreutils Shipped This Week: GNU 9.12 Gets Faster, Rust 0.12 Closes Compatibility Gaps

Two coreutils releases landed three days apart, and reading them together is more interesting than either alone.

GNU Coreutils 9.12 (September 14) brings performance improvements to sort, cut, and uniq, a new env option, safer terminal output, and a large set of bug fixes.

Rust Coreutils 0.12 (September 17) fixes every Ubuntu-prioritized issue, improves GNU compatibility, removes expr’s last C dependency, and expands Windows support.

The incumbent is not standing still

The framing usually applied to uutils is that a Rust rewrite is replacing aging C. GNU Coreutils 9.12 complicates that: sort, cut, and uniq all got faster, in code that has existed for decades and has been optimised repeatedly.

These are not obscure commands. sort in particular is on the hot path of an enormous number of shell pipelines, and it is one of the few coreutils where performance is regularly the bottleneck rather than an afterthought.

The safer terminal output work is worth noting separately. Commands that write untrusted data to a terminal can emit escape sequences that a terminal interprets, which is a genuine if underappreciated attack surface: ls on a directory whose filenames contain escape sequences has historically been able to do unpleasant things to your terminal state.

The replacement is closing the gap

“Fixes all Ubuntu-prioritized issues” is the significant phrase, because it describes a feedback loop that is actually working.

Ubuntu made uutils the default, which exposed it to an enormous number of real-world scripts. Incompatibilities surfaced, got prioritised, and are now being fixed. That is precisely the process that a rewrite needs and cannot get from a test suite alone, because the failures live in the accumulated edge cases nobody documented.

Removing expr’s last C dependency is a milestone of the same kind: the project eliminating its remaining reliance on the thing it replaces.

Why compatibility is the hard part

Reimplementing ls is not difficult. Reimplementing an ls that behaves identically to GNU’s in every case a twenty-year-old script depends on is very difficult indeed.

The dependencies are rarely deliberate. Exact output spacing that something is parsing with awk. A specific exit code distinguishing two failure modes. Error message wording that a script greps for. Locale-dependent behaviour that our locale guide covers, where sort produces different results under different collation and a script written on one machine produces wrong output on another.

None of that is documented as an interface. It became one by being relied upon.

Which are you running

ls --version
# "ls (GNU coreutils) 9.x"     -> GNU
# "ls (uutils coreutils) 0.x"  -> Rust

Worth checking on Ubuntu before debugging something odd after an upgrade. If a long-standing script started behaving differently, this is cheap to rule out, and GNU coreutils remains installable if you find a genuine incompatibility.

The honest read

The memory-safety argument for rewriting coreutils is weaker than for network-facing software, because cat is not parsing hostile input from the internet. The stronger arguments are the permissive licence and a modern codebase with a modern test suite.

The argument against has never really been about Rust. It is that mature, heavily-exercised software accumulates correctness that a rewrite discards, and you trade a known set of quirks for an unknown set of bugs.

What this week shows is that the experiment is running properly: the replacement is being tested at scale on a major distribution and fixing what it breaks, while the original continues to improve rather than being left to rot. That is a considerably healthier situation than either project existing alone.

Background reading

Explainers for the concepts behind this story.