64

Somewhere along the way I screwed up my ls command and now I get this ordering when running

$ ls -AhHl --color=auto
-rwxr-xr-x 1 clang clang  640 Mar  1 02:46 apple-touch-icon-precomposed.png
-rwxr-xr-x 1 clang clang  784 Jul 12 02:54 crossdomain.xml
-rwxr-xr-x 1 clang clang 1.2K Mar  1 02:46 favicon.ico
drwxr-xr-x 8 clang clang 4.0K Jul 12 23:50 .git
-rw-r--r-- 1 clang clang   17 Feb 29 19:48 .gitignore
-rwxr-xr-x 1 clang clang 1.4K Jul 12 02:54 humans.txt

What did I do that made ls ignore the dotfiles and instead order by first letter?

Output of locale:

$ locale
LANG=
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=en_US.UTF-8
clang1234
  • 867
  • 2
  • 10
  • 12

8 Answers8

56

⚠️ This answer is a little dated. Please check out the other answers, particularly those using aliases or ls -v.


Try adding

export LC_COLLATE="C"

in your dotfiles, or changing the LC_ALL assignment to:

export LC_ALL="C"

This controls the way sorting on character level works — while the default would be to sort dotfiles inline, this will make sort list dotfiles first.

However, note that this will basically stop support for your actual locale across all locale-aware utilities.


To go further, quoting the GNU Coreutils manual (emphasis mine):

If you use a non-POSIX locale (e.g., by setting LC_ALL to en_US), then sort may produce output that is sorted differently than you're accustomed to.

In that case, set the LC_ALL environment variable to C. Note that setting only LC_COLLATE has two problems. First, it is ineffective if LC_ALL is also set. Second, it has undefined behavior if LC_CTYPE (or LANG, if LC_CTYPE is unset) is set to an incompatible value. For example, you get undefined behavior if LC_CTYPE is ja_JP.PCK but LC_COLLATE is en_US.UTF-8.

slhck
  • 223,558
  • 70
  • 607
  • 592
  • 2
    Setting LC_ALL="C" did the trick! Thanks for the quick response – clang1234 Jul 13 '12 at 01:20
  • 9
    Setting LC_ALL="C" will `ls` foreign language (e.g. Japanese) unicode filenames as `????????` – ohho Jul 23 '13 at 08:31
  • 1
    Note that by adding `export LC_ALL="C"` to your dotfiles you'll effectively lose support for your actual locale across all locale-aware utilities. While the _printing_ problem that @ohho points out could be remedied with [Faroul's answer](http://superuser.com/a/656746/139307), you'll introduce _sorting_ problems: sorting will happen by byte value only, resulting in sorting that is (a) unexpectedly _case-sensitive_, and (b) invariably places accented chars. _after_ all non-accented ones. – mklement0 Nov 03 '14 at 04:16
  • 2
    I think this answer is a little out of date. At the very least use `C.UTF-8`, as in @Faroul's [answer](https://superuser.com/a/656746/238591), as the old non-Unicode variant will cause trouble for things like Python 3 output. But do note, as others have, that locale affects _everything_, not just `ls`, so setting the locale to `C` or `C.UTF-8` will e.g. disable spell checking. So best to set your locale appropriately, e.g. to `en_US.UTF-8`, and go for a solution that only affects `ls` (like using aliases and the `-v` flag as in @VinceValenti's [answer](https://superuser.com/a/1364549/238591)). – George Hawkins Apr 26 '20 at 13:43
  • thank you very much for `ls -v` – Luke Jul 01 '20 at 06:58
  • thanks for `ls -v` – Luke Jun 29 '21 at 11:58
43

To avoid any system wide changes without real need, one can change only the way how ls works for the current user by adding the alias to the .bashrc:

alias ll='LC_COLLATE=C ls -alF'

This sorts dot files first, allows to properly handle (show and sort) "uncommon" character sets like cyrillic. The only culprit that the sorting will be case-sensitive.

Source: http://ubuntuforums.org/showthread.php?t=816753

Andrey Loskutov
  • 571
  • 4
  • 2
  • 3
    This is the best answer here by far – Software Engineer Mar 29 '15 at 13:16
  • Beautiful. Much better than changing the system-wide configuration (which can cause you problems with some python modules). – Gui Ambros May 28 '16 at 19:56
  • 2
    This was the most helpful answer. Additionally, in an environment where `LC_ALL` is already set to something incompatible with sorting/hoisting dotfiles to the top of the `ls` listing, use `alias ll='LC_ALL=C ls -alF'` instead, because `LC_COLLATE` doesn't override `LC_ALL`. – matty Jul 06 '16 at 08:17
27

The ls(1) manpage lists:

-v natural sort of (version) numbers within text

This seems change how periods are sorted and does group dotfiles first. I have:

alias ls='ls -vAF'
alias ll='ls -l'

in my ~/.bashrc.

Vince Valenti
  • 271
  • 3
  • 2
10

Setting

LC_ALL="C.UTF-8"

works fine for me - umlauts and "ls -la" lists dot-files first.

Faroul
  • 109
  • 1
  • 2
  • 3
    dot files are listed first, but now file names that starts with a uppercase letter are listed before those that starts with a lowercase. – Chnossos Aug 02 '14 at 20:47
  • 1
    +1 for coming closer than the accepted answer, but to summarize the limitations: the sorting will become _case-sensitive_, and foreign characters - while now *printed* correctly due to `UTF-8` - won't be *sorted* correctly. – mklement0 Nov 03 '14 at 04:16
  • 3
    `C.UTF-8` is an invalid locale, so locale reverts back to `C`. You still lose UTF-8 support. In fact, it's even worse since `LC_ALL=C` mostprograms display most multibyte characters *correctly*, but with `LC_ALL=something_invalid` some programs don't (like `ls). – Martin Tournoij Jun 07 '15 at 23:54
  • @MartinTournoij but if [Stéphane Chazelas says otherwise about GNU systems](https://unix.stackexchange.com/a/194169/70524) (so presumably Debian and Linux in general), which is correct? – muru Jul 09 '18 at 15:49
  • @muru I'm not sure, I guess you'll have to ask Stéphane? All I know is that `locale` seems to error out with `LC_ALL=C.UTF-8`: https://gist.github.com/Carpetsmoker/ef09b3734b29372939f97107413d7733 – that is on Arch Linux. – Martin Tournoij Jul 09 '18 at 16:09
  • @MartinTournoij seems it's not enabled on Arch: https://bugs.archlinux.org/task/32296 (it is on Debian and co, and Fedora as well: https://bugzilla.redhat.com/show_bug.cgi?id=902094) – muru Jul 09 '18 at 16:20
  • @muru Looks like [they included a custom patch against GNU libc](https://sourceware.org/bugzilla/show_bug.cgi?id=17318#c3), so it's something that may or may not work depending on your Linux/Unix flavour and libc that's being used. – Martin Tournoij Jul 09 '18 at 17:05
  • +1 for being creative. Did not work though. –  Apr 27 '19 at 08:17
1

An off the wall idea

Disclaimer: This is going to be overkill for most of you. But I've been doing this since 1995 and I have the skills to make my world exactly how I want it. So, I why not?

I really like using different sorting methods with ls, especially -rt (which is sort by reverse modified time). So, I decided to try something crazy and use awk to do my sorting.

# save as ~/.ls.awk
# inpsired by http://superuser.com/questions/448291/how-can-i-make-ls-show-dotfiles-first

{
    if($1 == "total"){
        print $0
        next
    }
    # may need to adjust $9 to match your name column
    if(match($9, /^(\033\[[0-9]*m)*\./)) # optionally look past xterm highlighting like: ^[[34m
        df[++dd] = $0
    else
        nf[++nn] = $0
}
END{
    while (++d in df)
        print df[d]
    while (++n in nf)
        print nf[n]
}

Now instead of defining a bash alias, I define a bash function (because aliases can only append arguments at the end, but functions can use them anywhere)

ll(){ CLICOLOR_FORCE=1 ls -lhA $* | awk -f ~/.ls.awk; }

To see the results

Let's create some sample files:

for n in 4 .4 3 .3 2 .2 1 .1; do touch $n; sleep 1; done

Using plain ls

$ ls -lA
total 8
-rw-r--r--  1 bbronosky  staff    0 Dec  1 00:25 .1
-rw-r--r--  1 bbronosky  staff    0 Dec  1 00:25 .2
-rw-r--r--  1 bbronosky  staff    0 Dec  1 00:25 .3
-rw-r--r--  1 bbronosky  staff    0 Dec  1 00:25 .4
-rwxr-xr-x  1 bbronosky  staff  285 Nov 29 13:14 .ls.awk
-rwxr-xr-x  1 bbronosky  staff    0 Dec  1 00:25 1
-rwxr-xr-x  1 bbronosky  staff    0 Dec  1 00:25 2
-rwxr-xr-x  1 bbronosky  staff    0 Dec  1 00:25 3
-rwxr-xr-x  1 bbronosky  staff    0 Dec  1 00:25 4
$ ls -lArt

total 8
-rwxr-xr-x  1 bbronosky  staff  285 Nov 29 13:14 .ls.awk
-rwxr-xr-x  1 bbronosky  staff    0 Dec  1 00:25 4
-rw-r--r--  1 bbronosky  staff    0 Dec  1 00:25 .4
-rwxr-xr-x  1 bbronosky  staff    0 Dec  1 00:25 3
-rw-r--r--  1 bbronosky  staff    0 Dec  1 00:25 .3
-rwxr-xr-x  1 bbronosky  staff    0 Dec  1 00:25 2
-rw-r--r--  1 bbronosky  staff    0 Dec  1 00:25 .2
-rwxr-xr-x  1 bbronosky  staff    0 Dec  1 00:25 1
-rw-r--r--  1 bbronosky  staff    0 Dec  1 00:25 .1

Using my function that filters with awk

$ ll
total 8
-rw-r--r--  1 bbronosky  staff     0B Dec  1 00:25 .1
-rw-r--r--  1 bbronosky  staff     0B Dec  1 00:25 .2
-rw-r--r--  1 bbronosky  staff     0B Dec  1 00:25 .3
-rw-r--r--  1 bbronosky  staff     0B Dec  1 00:25 .4
-rwxr-xr-x  1 bbronosky  staff   285B Nov 29 13:14 .ls.awk
-rwxr-xr-x  1 bbronosky  staff     0B Dec  1 00:25 1
-rwxr-xr-x  1 bbronosky  staff     0B Dec  1 00:25 2
-rwxr-xr-x  1 bbronosky  staff     0B Dec  1 00:25 3
-rwxr-xr-x  1 bbronosky  staff     0B Dec  1 00:25 4

$ ll -rt
total 8
-rwxr-xr-x  1 bbronosky  staff   285B Nov 29 13:14 .ls.awk
-rw-r--r--  1 bbronosky  staff     0B Dec  1 00:25 .4
-rw-r--r--  1 bbronosky  staff     0B Dec  1 00:25 .3
-rw-r--r--  1 bbronosky  staff     0B Dec  1 00:25 .2
-rw-r--r--  1 bbronosky  staff     0B Dec  1 00:25 .1
-rwxr-xr-x  1 bbronosky  staff     0B Dec  1 00:25 4
-rwxr-xr-x  1 bbronosky  staff     0B Dec  1 00:25 3
-rwxr-xr-x  1 bbronosky  staff     0B Dec  1 00:25 2
-rwxr-xr-x  1 bbronosky  staff     0B Dec  1 00:25 1

You can see my implementation of this here https://github.com/RichardBronosky/dotfiles/commit/6170c0a9

What's most important about this is that it is a framework for tweaking your ls output. You can do anything you want with that awk filter. You might want it to be directories, then dotfiles, then everything else. Once you know how to handle your xterm colors, it's not too difficult. It's totally up to you.

Bruno Bronosky
  • 1,885
  • 1
  • 20
  • 26
1

The following line shows (dot-)directories first and the (dot-)files. I had trouble to find out the right solution within the other answers so I wanted to clarify it:

ls -vAFl --group-directories-first

This shows

  • dot-dirs first
  • then other dirs (but case sensitive: first upper- then lower-case)
  • dot-files
  • non-dot files
Jesko
  • 11
  • 3
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 29 '22 at 17:28
0

ls -lvAX

-l : use a long listing format

-v : natural sort of (version) numbers within text

-A : do not list implied . and ..

-X : sort alphabetically by entry extension

-1

Might try this in your .bashrc or /etc/bashrc file:

LS_OPTIONS='--color=tty -A -F -X -B -h -v -b -T 0 --group-directories-first';
export LS_OPTIONS;
alias ls='/bin/ls $LS_OPTIONS';

This assumes you're running a somewhat newer version of ls that takes "--group-directories-first" as an option. You can obviously tweak the LS options to your liking.

Phil
  • 1