Emacs, Dired, and Ubuntu 26.04

After a clean install of Ubuntu 26.04, Emacs Dired began showing chaotic, unsorted directory listings. The problem isn't Emacs 30.2—it's a known issue in the new Rust coreutils.

Just recently I upgraded one of my Dell laptops to the latest Ubuntu 26.04 LTS with a clean install. The installation process was smooth and enjoyable.

Ubuntu 26.04 ships with Emacs 30.2, which made me happy, so I installed it immediately—more precisely, the emacs-pgtk Wayland-optimized package, since Wayland is the default windowing system on Ubuntu 26.04.

The Problem

I started Emacs and, after a short time, ran into a problem: Dired (C-x d) did not work properly. It displayed a strange mess of unsorted, chaotic entries, as shown below. It was a big surprise.

Click or tap to view the full-size picture.

I’ve been using the same .emacs multi-platform config file for Linux, macOS, and Windows for years and have never had any problems with Dired on different platforms, let alone across versions of the same OS.

My Dired configuration is quite simple:

;;; `DIRED'
(require 'dired)

;; Dired options for Windows and non-Windows (Linux, macOS) systems.
;; http://oremacs.com/2015/01/13/dired-options/
(if (string-equal system-type "windows-nt")
    (setq dired-listing-switches "/A:H /O:NG")
  (setq dired-listing-switches "-laGgh1v --group-directories-first --time-style=long-iso"))

;; `dired-subtree' (since 2022-10-18)
;; https://xenodium.com/drill-down-emacs-dired-with-dired-subtree/
(use-package dired-subtree
  :ensure t
  :after dired
  :config
  (bind-key "<tab>" #'dired-subtree-toggle dired-mode-map)
  (bind-key "<backtab>" #'dired-subtree-cycle dired-mode-map))

;; By default, Dired buffers will not update themselves
;; unless you explicitly tell them to.
(setq dired-auto-revert-buffer t)

The dired-listing-switches variable defines the switches passed to dir or ls for Dired on Windows and Linux/macOS systems, respectively. I’ve been using this configuration for years.

On Ubuntu 24.04 everything looks fine. So, what’s wrong with Emacs 30.2?

I decided to test the ls switches in the console. Oops—the same result as in Dired under Emacs 30.2!

Click or tap to view the full-size picture.

This means the problem is not with the new version of Emacs. After a short search, the answer was found.

The Bug in Ubuntu 26.04

It turns out the bug in Ubuntu 26.04 is real, documented, and already tracked in Ubuntu’s Rust-based coreutils for Ubuntu 26.04. The issue specifically affects ls when using --group-directories-first and is tied to missing locale collation support in the Rust rewrite.

Launchpad Bug 2154042 — Incorrect alphabetical sorting with --group-directories-first

This is the main bug report affecting Ubuntu 26.04 users after the Rust coreutils transition. It explicitly states that:

  1. ls from the Rust-based coreutils does not sort alphabetically when using --group-directories-first.
  2. The bug appears both when LC_ALL is set and when it is unset.
  3. GNU ls (gnuls) sorts correctly; Rust ls does not.

Conclusion

Since Dired—and possibly other Emacs packages—uses ls to list directories and files, Dired will not work properly on Ubuntu 26.04 regardless of the Emacs version.

Solution

Luckily, the Ubuntu team has kept the old version of coreutils (GNU coreutils), written in good old C. The GNU version of ls is gnuls and is installed by default on Ubuntu 26.04. This means we can use gnuls—which works properly—for Dired instead of ls when running Ubuntu 26.04.

To do this, we will use the insert-directory-program variable, which holds the absolute or relative name of the ls-like program. This variable is used by insert-directory and dired-insert-directory (and therefore by Dired). Its default value on Linux and macOS is ls.

First we need to determine the name and version of the OS. The custom ubuntu-version function helps with that:

(defun ubuntu-version ()
  "Return (MAJOR MINOR) as integers if running on Ubuntu, else nil."
  (when (and (eq system-type 'gnu/linux)
             (file-readable-p "/etc/os-release"))
    (with-temp-buffer
      (insert-file-contents "/etc/os-release")
      (let* ((os-release (buffer-string))
             (is-ubuntu (string-match-p "^ID=ubuntu" os-release))
             (version-id (and is-ubuntu
                              (when (string-match "VERSION_ID=\"\\([0-9]+\\)\\.\\([0-9]+\\)\"" os-release)
                                (list (string-to-number (match-string 1 os-release))
                                      (string-to-number (match-string 2 os-release)))))))
        version-id))))

On my machine ubuntu-version returns (26 4).

Now we can replace ls with gnuls if we are on Ubuntu 26.x:

(when (eq (car (ubuntu-version)) 26)
  (setq insert-directory-program "gnuls")
  (message "`ls' is replaced with `gnuls' on Ubuntu 26.x."))

After that, Dired works as before.

Click or tap to view the full-size picture.

Happy Emacsing on Ubuntu 26.04!

– The Emacs Cat.