5

Is there a nice way to sort directory contents (including hidden files) in the shell? Basically i'd like to be able to ls directories just its done in my GUI file manager. In a typical directory, the output is as such:

.a_hidden_dir

.b_hidden_dir

.c_hidden_dir

a_dir

b_dir

c_dir

.a_hidden_file

.b_hidden_file

.c_hidden_file

a_file

b_file

c_file

Of course ls has the --group-directories-first option, but this only gets us part of the way there as sort ignores the leading ., it does not sort hidden files to the top.

I'd like to be able to sort output from ls, find, or other list of paths in such a way. Does anyone know a good way to do this - maybe a sort -k KEYDEF?

Right now I'm doing something like this (it assumes directory names have a slash append to them):

pathsort(){
    input=$(cat)
    (
        awk '/^\..+\/$/' <<<"$input" | sort
        awk '/^[^.].+\/$/' <<<"$input" | sort
        awk '/^\..+[^/]$/' <<<"$input" | sort
        awk '/^[^.].+[^/]$/' <<<"$input" | sort
    ) | sed 's/\/$//'
}

\ls -Ap | pathsort

The above code gets the job done, but it is far from ideal. Please tell me there is a better way...

Community
  • 1
  • 1
Six
  • 5,122
  • 3
  • 29
  • 38
  • 1
    Post the output you expect. – Rajesh N May 04 '15 at 06:28
  • ls -la isn't working? – Nachiket Kate May 04 '15 at 06:50
  • Sample input can be obtained from running `\ls -Ap`. Expected sample output is in the first quoted block of text above (starting with .a_hidden_dir). `ls -la` "works" of course, but I am trying to get the output sorted, with hidden stuff at the top, as it is in a file manager. I want it ordered hidden directories, directories, hidden files, files. – Six May 04 '15 at 06:52
  • 2
    Have you tried setting LANG=C in the environment so that the sort is done in C order, with the names with leading dots appearing before most other names. – Jonathan Leffler May 04 '15 at 06:58
  • @JonathanLeffler Ahh thank you, that was so simple! I have used LC_ALL=C with `sort` for reproducible builds before but didn't know about LANG=C. `LANG=C \ls -A --group-directories-first` was exactly what I needed... And here I was jerry rigging some terrible contraption. Thanks again! – Six May 04 '15 at 07:05
  • You could use `LC_ALL` instead of, or as well as, `LANG` with both `ls` and `sort`. You should also be able to use `LC_COLLATE` too; that's the most precise. – Jonathan Leffler May 04 '15 at 07:50

2 Answers2

5

Jonathan Leffler proposed a simple and functional solution in a comment: set the local environment variable LANG=C. On my system, the default LANG=en_US.UTF-8 results in undesirable pathname sorting characteristics. The C is apparently in reference to bytewise character sorting with an ASCII charset. The result of setting LANG=C is such that 'dotfiles' (to include directories) are sorted to the top. It may be useful to note that LC_ALL=C may be used as well, as LC_ALL is a superset of LANG and other LC_* variables. All in all, setting the locale to C for sorting commands is strongly advised if you'd like a consistent sorting experience.

Here is the final solution to the desired pathname sorting hierarchy (dotfile dirs > normal dirs > dotfile files > normal files):

LC_ALL=C ls -A --group-directories-first

Note: this includes symlinks to files and directories as well

Similarly for sorting any other source of pathname output:

findtool | LC_ALL=C sort
Community
  • 1
  • 1
Six
  • 5,122
  • 3
  • 29
  • 38
0

Funnily, I think sorting the directories is simplest:

ls -1d .*/; ls -1d */

Files are harder to separate from the directories, you need to use find:

(find . -maxdepth 1 -type f -name '.*' -printf '%P\n' | sort); (find . -maxdepth 1 -type f -not -name '.*' -printf '%P\n' | sort)

Put the whole thing together:

alias lss="
    ls -1d .*/;   # Hidden directories
    ls -1d */;    # Normal directories
    find . -maxdepth 1 -type f -name '.*' -printf '%P\n' | sort;
                  # Hidden files
    find . -maxdepth 1 -type f -not -name '.*' -printf '%P\n' | sort
                  # Normal files
"

One caveat: There are other items that this will miss, like links and devices.

chw21
  • 7,970
  • 1
  • 16
  • 31
  • 1
    Your lsd looks good. I had done something very similar to the second example with lsd `(find -L -maxdepth 1 -type d -name '.*' -printf '%P\n' | sort; find -L -maxdepth 1 -mindepth 1 -type d ! -name '.*' -printf '%P\n' | sort)` and lsf `(find -L -maxdepth 1 -type f -name '.*' -printf '%P\n' | sort; find -L -maxdepth 1 -type f ! -name '.*' -printf '%P\n' | sort)`. The `-L` will catch links to dirs/files. I wanted to be able to sort other output also... turns out this was making something really complicated out of nothing. You can get the desired sorting via `LC_ALL=C \ls -A --g`. :P – Six May 04 '15 at 08:24
  • Yes, maybe you should answer your own question with the update and mark it answered. That way other people that might have a similar question will see your answer. – chw21 May 04 '15 at 09:41