Want to know whether your script behaves differently?
Rust Coreutils vs GNU: What Breaks Scripts runs any command line under both implementations and shows the exit codes, stderr and output that differ, with the differences found so far.
After an upgrade to Ubuntu 26.04, ls, sort and timeout are different programs with the same names. The Rust rewrite (uutils) aims to be a drop-in replacement and mostly is, so nothing looks wrong until one exit code or error message changes inside a script nobody reads. The first question when a script misbehaves after the upgrade is which implementation ran it, and the answer is not the same for every command.
Which Commands Are Rust on Ubuntu 26.04?
Reproduced on 2026-10-10 in an ubuntu:26.04 container on this machine (Ubuntu 26.04.1 LTS, rust-coreutils 0.10.0-1ubuntu2~26.04.1, gnu-coreutils 9.7-3ubuntu2.1, bash 5.3.9). The symlinks tell most of the story:
ls points into /usr/lib/cargo/bin/coreutils, where the uutils binaries live. cp points at gnucp, the GNU binary. The script below checks every command the same way. On this image, 99 were Rust and 5 were GNU: cp, df, mv, rm and true. Ubuntu 26.10 moved cp, mv and rm to Rust as well (reported on Hacker News), so expect that list to shrink on the next release.
The Script
Save as which-coreutils.sh. With no arguments it checks every command coreutils ships; give it names to check only those.
Prerequisites
bash 4 or later (associative arrays). dpkg-query for the package lines, which every Debian or Ubuntu box has; elsewhere the script still classifies the commands and skips the package section. No root.
How Does Each Check Work?
type -Pfinds the file a script would execute, skipping aliases and shell builtins. That matters forecho,printf,test,[andpwd: bash runs its own builtins for those, so a script that callsechonever touches the coreutils binary at all, Rust or GNU.readlink -ffollows the symlinks to the real file. A target under/usr/lib/cargo/bin/coreutils/is uutils; a target namedgnu<command>is GNU.--versionsettles everything else, including distributions that install either implementation directly in/usr/bin. GNUtestignores--version, so the script asks its twin[instead.dpkg-queryshows whichcoreutils-from-*package is installed, which is what decides the plain names, and prints the matching switch command.
What Does the Script Print?
On the default 26.04.1 image:
On Debian 13 (debian:stable-slim, the same script) all 104 commands report GNU coreutils 9.7 and there is no switch line, because Debian has not changed its default.
How Do I Switch Back to GNU Coreutils?
The obvious command fails:
Both packages provide coreutils-from, so one has to leave in the same transaction. The trailing - on a package name tells apt to remove it, and --allow-remove-essential is needed because coreutils-from-uutils is marked essential. The container ran it with -y; on a real server, read the removal list apt prints before you confirm:
Only the links change. rust-coreutils stays installed, and the reverse command, sudo apt install coreutils-from-uutils coreutils-from-gnu- --allow-remove-essential, put ls back to uutils 0.10.0 in the same container.
Switching back is a fix for this week, not for the next release. To find the commands in your own scripts that behave differently, and to keep using the default, run them through the side-by-side comparison. The rest of the 26.04 changes, including sudo-rs, are in Ubuntu 26.04: What Breaks in Bash Scripts.
Frequently Asked Questions
Does Ubuntu 26.04 use Rust coreutils?
Yes. Ubuntu 26.04 LTS ships coreutils-from-uutils as the default, so most coreutils commands are the Rust uutils implementation (0.10.0 in 26.04.1). Not all: in a fresh 26.04.1 container, cp, mv, rm, df and true were still GNU coreutils 9.7. Run ls --version or readlink -f on the command to see which one you have.
How do I switch back to GNU coreutils on Ubuntu 26.04?
Swap the two packages in one transaction: sudo apt install coreutils-from-gnu coreutils-from-uutils- --allow-remove-essential. The trailing minus removes coreutils-from-uutils, and --allow-remove-essential is required because it is an essential package. A plain apt install coreutils-from-gnu fails with Conflicts: coreutils-from. Afterwards ls --version reports GNU coreutils 9.7.
What is the difference between rust-coreutils and coreutils-from-uutils?
rust-coreutils is the package that contains the uutils binaries, under /usr/lib/cargo/bin/coreutils. coreutils-from-uutils is the small package that points /usr/bin/ls and the rest at them. Likewise gnu-coreutils holds the GNU binaries as gnuls, gnusort and so on, and coreutils-from-gnu points the plain names at those. Both sets of binaries can be installed at once; only one of the two from packages can be.
How can I run the GNU version of one command without switching?
Call it with the gnu prefix: gnusort, gnudate, gnutimeout and so on. On 26.04 the gnu-coreutils package keeps every GNU binary installed under those names even while uutils is the default, which makes it easy to compare one command, or to pin one script to GNU behaviour while you fix it.
Will switching back to GNU coreutils break future upgrades?
The packages are designed to be swapped, and the reverse command restores uutils: sudo apt install coreutils-from-uutils coreutils-from-gnu- --allow-remove-essential. Both directions were run in a 26.04 container for this page. The risk is staying off the default for good: Ubuntu 26.10 moved cp, mv and rm to Rust as well, so treat GNU as time to fix scripts, not a permanent setting.
Part of the bash snippets collection
Related Scripts
- Rust Coreutils vs GNU: What Breaks Scripts — run a command line under both and see every difference
- Ubuntu 26.04: What Breaks in Bash Scripts — coreutils, sudo-rs and a pre-upgrade checklist
- Bash Timeout Command — one of the commands whose exit codes changed
- Fix "bash: command not found" — when a script finds a different binary than you expected