Skip to content

Ubuntu 26.04 Rust Coreutils: Check Which One You Run and Switch Back to GNU

ubuntucoreutilsuutilsupgradetroubleshooting
5 min read

Quick Answer

Ubuntu 26.04 LTS installs coreutils-from-uutils by default, so ls, sort, date, timeout, base64 and about 95 other commands run the Rust rewrite (uutils 0.10.0 in 26.04.1), while cp, mv, rm, df and true stay GNU 9.7. Check one command with ls --version or readlink -f $(command -v sort): a path under /usr/lib/cargo/bin/coreutils is Rust. GNU stays installed under gnu-prefixed names, gnuls and gnusort, for one-off comparisons. To switch everything back, replace the package in one apt transaction: sudo apt install coreutils-from-gnu coreutils-from-uutils- --allow-remove-essential. A plain apt install coreutils-from-gnu fails with Conflicts: coreutils-from, because the two packages provide the same thing and the trailing minus is what removes the other. The reverse command, coreutils-from-uutils coreutils-from-gnu-, switches back again. which-coreutils.sh on this page lists the implementation behind every coreutils command on a box in one run.

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:

text
$ ls -l /usr/bin/ls /usr/bin/cp lrwxrwxrwx /usr/bin/cp -> gnucp lrwxrwxrwx /usr/bin/ls -> ../lib/cargo/bin/coreutils/ls

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.

bash
#!/bin/bash # Script: which-coreutils.sh # Purpose: After an upgrade to Ubuntu 26.04 ls, sort, date and timeout are Rust (uutils) while cp, mv and rm may still be GNU, and nothing tells you which one a script just ran; this lists it per command. # Usage: ./which-coreutils.sh [COMMAND ...] (default: every command coreutils ships) set -euo pipefail CHECK="✓" CROSS="✗" COREUTILS=(arch b2sum base32 base64 basename basenc cat chcon chgrp chmod chown chroot cksum comm cp csplit cut date dd df dir dircolors dirname du echo env expand expr factor false fmt fold groups head hostid id install join link ln logname ls md5sum mkdir mkfifo mknod mktemp mv nice nl nohup nproc numfmt od paste pathchk pinky pr printenv printf ptx pwd readlink realpath rm rmdir runcon seq sha1sum sha224sum sha256sum sha384sum sha512sum shred shuf sleep sort split stat stdbuf stty sum sync tac tail tee test timeout touch tr true truncate tsort tty uname unexpand uniq unlink users vdir wc who whoami yes) CMDS=("$@") [[ $# -gt 0 ]] || CMDS=("${COREUTILS[@]}") declare -A BY_IMPL=() for cmd in "${CMDS[@]}"; do # type -P skips aliases and builtins: echo, printf, test and pwd are bash builtins, and scripts that call # them by name never reach the binary at all, so what matters is the file on disk. bin=$(type -P "$cmd" || true) if [[ -z "$bin" ]]; then BY_IMPL["missing"]+=" $cmd"; continue; fi real=$(readlink -f "$bin") case "$real" in */cargo/bin/coreutils/*|*/coreutils/uutils*) impl="uutils (Rust)" ;; */busybox) impl="busybox" ;; */gnu"$cmd") impl="GNU" ;; *) # Elsewhere ask the binary itself; GNU test ignores --version, so ask its twin [ instead. probe=$real [[ "$cmd" == test && -x "${real%/*}/[" ]] && probe="${real%/*}/[" ver=$(timeout 2 "$probe" --version 2>/dev/null | head -1 || true) case "$ver" in *uutils*) impl="uutils (Rust)" ;; *GNU*|*"(coreutils)"*) impl="GNU" ;; # Debian's dd says "dd (coreutils) 9.7", without GNU *) impl="other" ;; esac ;; esac BY_IMPL["$impl"]+=" $cmd" done for impl in "uutils (Rust)" "GNU" "busybox" "other" "missing"; do [[ -n "${BY_IMPL[$impl]:-}" ]] || continue read -ra list <<< "${BY_IMPL[$impl]}" printf '%s %-14s %3d: %s\n' "$([[ $impl == missing ]] && echo "$CROSS" || echo "$CHECK")" "$impl" "${#list[@]}" "${list[*]}" | fold -s -w 110 done for probe in ls cp; do if bin=$(type -P "$probe"); then echo " $probe --version: $("$bin" --version 2>/dev/null | head -1)"; fi done # The switch is a package choice, not per command: coreutils-from-uutils and coreutils-from-gnu conflict. if command -v dpkg-query >/dev/null; then echo " packages:" dpkg-query -W -f=' ${db:Status-Abbrev} ${Package} ${Version}\n' \ coreutils coreutils-from-uutils coreutils-from-gnu rust-coreutils gnu-coreutils 2>/dev/null | grep '^ ii' || true if dpkg-query -W -f='${db:Status-Abbrev}' coreutils-from-uutils 2>/dev/null | grep -q '^ii'; then echo " back to GNU: sudo apt install coreutils-from-gnu coreutils-from-uutils- --allow-remove-essential" elif dpkg-query -W -f='${db:Status-Abbrev}' coreutils-from-gnu 2>/dev/null | grep -q '^ii'; then echo " back to uutils: sudo apt install coreutils-from-uutils coreutils-from-gnu- --allow-remove-essential" fi fi if [[ -x /usr/bin/gnuls ]]; then echo " GNU copies stay installed as gnu<name> (gnuls, gnusort, gnudate...) for one-off comparisons" fi

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 -P finds the file a script would execute, skipping aliases and shell builtins. That matters for echo, printf, test, [ and pwd: bash runs its own builtins for those, so a script that calls echo never touches the coreutils binary at all, Rust or GNU.
  • readlink -f follows the symlinks to the real file. A target under /usr/lib/cargo/bin/coreutils/ is uutils; a target named gnu<command> is GNU.
  • --version settles everything else, including distributions that install either implementation directly in /usr/bin. GNU test ignores --version, so the script asks its twin [ instead.
  • dpkg-query shows which coreutils-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:

text
$ which-coreutils.sh ✓ uutils (Rust) 99: arch b2sum base32 base64 basename basenc cat chcon chgrp chmod chown chroot cksum comm csplit cut date dd dir dircolors dirname du echo env expand expr factor false fmt fold groups head hostid id install join link ln logname ls md5sum mkdir mkfifo mknod mktemp nice nl nohup nproc numfmt od paste pathchk pinky pr printenv printf ptx pwd readlink realpath rmdir runcon seq sha1sum sha224sum sha256sum sha384sum sha512sum shred shuf sleep sort split stat stdbuf stty sum sync tac tail tee test timeout touch tr truncate tsort tty uname unexpand uniq unlink users vdir wc who whoami yes ✓ GNU 5: cp df mv rm true ls --version: ls (uutils coreutils) 0.10.0 cp --version: cp (GNU coreutils) 9.7 packages: ii coreutils 9.5-1ubuntu2+0.0.0~ubuntu25 ii coreutils-from-uutils 0.0.0~ubuntu25 ii gnu-coreutils 9.7-3ubuntu2.1 ii rust-coreutils 0.10.0-1ubuntu2~26.04.1 back to GNU: sudo apt install coreutils-from-gnu coreutils-from-uutils- --allow-remove-essential GNU copies stay installed as gnu<name> (gnuls, gnusort, gnudate...) for one-off comparisons exit=0

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:

text
$ sudo apt install coreutils-from-gnu The following packages have unmet dependencies: coreutils-from-gnu : Conflicts: coreutils-from coreutils-from-uutils : Conflicts: coreutils-from E: Unable to satisfy dependencies. Reached two conflicting assignments: 1. coreutils-from-gnu:amd64=0.0.0~ubuntu25 is selected for install 2. coreutils-from-gnu:amd64=0.0.0~ubuntu25 is not selected for install because: 1. coreutils-from-uutils:amd64 is selected for install 2. coreutils-from-uutils:amd64 Conflicts coreutils-from [selected coreutils-from-uutils:amd64]

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:

text
$ sudo apt install coreutils-from-gnu coreutils-from-uutils- --allow-remove-essential The following packages will be REMOVED: coreutils-from-uutils The following NEW packages will be installed: coreutils-from-gnu WARNING: The following essential packages will be removed. coreutils-from-uutils 0 upgraded, 1 newly installed, 1 to remove and 2 not upgraded. Removing coreutils-from-uutils (0.0.0~ubuntu25) ... Setting up coreutils-from-gnu (0.0.0~ubuntu25) ... $ which-coreutils.sh sort timeout date ls cp ✓ GNU 5: sort timeout date ls cp ls --version: ls (GNU coreutils) 9.7 cp --version: cp (GNU coreutils) 9.7

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

Raw script, MIT licensed: scripts/rust-coreutils-ubuntu.sh on GitHub

PAID RESOURCE — $9

The Production Bash Toolkit

An operational script system + a 30-function shared library + a 52-page field guide. The production layer the free snippets don't cover.

Get the Toolkit →
curl -O bashlib-starter.sh

Get the bashlib starter

Ten functions I source into every script on my own boxes — strict-mode setup, an ERR trap that names the failing line, lock and timeout wrappers, and cleanup that runs on every exit path. One email, no sequence.

BashSnippets logo

Written by Travis

Creator of BashSnippets.xyz

bashsnippets.xyz/about

Related Snippets

Frequently Asked Questions

faq — snippet

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.

faq — snippet

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.

faq — snippet

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.

faq — snippet

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.

faq — snippet

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.