🦬

EmacsのDiredでの日本語ファイル名の並びの異常を直す

2024/01/13に公開

問題

Dired モードで、

(setq dired-listing-switches "-alhv")

としているときファイル名の順にならない。同じ接頭辞で揃えているのに揃わないので非常に不便である。

原因

どうやら LC_* な環境変数が影響して Emacs の外でも並びが壊れているのがわかる。

ls -v

次のように LC_* を強制的に C とすると正しく動く。

LC_ALL=C ls -v

LC_ALL は LC 族のなかでも最強。したがって、これを設定すればすべての LC 族を倒したことになる。

そこで Emacs に戻ってどうするか?

対策1. 外部コマンドを変更する

(setq insert-directory-program "LC_ALL=C ls")

とは書けないので、

~/.emacs.d/dired-ls
#!/bin/sh
LC_ALL=C exec ls "$@"

を用意して、

(setq insert-directory-program "~/.emacs.d/dired-ls")

ls のかわりに呼び出す。

対策2. 内部で環境変数を一時的に変更する

(let ((process-environment (cons "LC_ALL=C" process-environment)))
  (call-process "/usr/bin/env" nil (current-buffer)))

とすれば一時的に環境変数を変更できるので、

(defadvice insert-directory (around insert-directory-around activate)
  (let ((process-environment (cons "LC_ALL=C" process-environment)))
    ad-do-it))
;; (ad-disable-advice 'insert-directory 'around 'insert-directory-around)
;; (ad-update 'insert-directory)

として、一覧表示メソッド insert-directory をラップする。

対策3. coreutils の gls を使う

Mac に最初から入っている微妙に仕様が異なるコマンド軍のなかの ls を使うのではなく、brew で入る coreutils に入っている gls コマンドを使うようにする。

(setq insert-directory-program "gls")

それだけでうまくいく。

これも面倒な場合は、もしくはこんな災いをあらかじめ回避するためには、coreutils が用意した、既存コマンドを置き換えるためのシンボリックリンクが詰まったディレクトリに優先的にパスを通してしまえば、そもそも ls は gls を呼ぶようになるので何もしなくてよくなる。

まとめ

  • Mac で最初から入っている ls の -v オプションは LC_ALL=C ls -v のときだけ正しく動く
  • Mac で最初から入っている BSD 経由のコマンド軍は、オプションだけでなく、文字コードの扱いもレガシーなままだった
  • そもそも gls に置き換えるだけでよかった (なんなら全置き換えしよう)

Discussion