7

ls --group-directories-first displays all directories before files, but if there is a symlink to a directory, it is displayed among the files. Is there a way to group directory-symlinks with other regular directories?

3

2 Answers 2

5

Consider using -L. For example:

$ ls -l
total 4
-rw-r--r-- 1 ja users    0 Dec 20 13:23 A
drwxr-xr-x 2 ja users 4096 Dec 20 13:23 b
lrwxrwxrwx 1 ja users    1 Dec 20 13:23 sl -> b
$ ls --group-directories-first -l
total 4
drwxr-xr-x 2 ja users 4096 Dec 20 13:23 b
-rw-r--r-- 1 ja users    0 Dec 20 13:23 A
lrwxrwxrwx 1 ja users    1 Dec 20 13:23 sl -> b
$ ls --group-directories-first -lL
total 8
drwxr-xr-x 2 ja users 4096 Dec 20 13:23 b
drwxr-xr-x 2 ja users 4096 Dec 20 13:23 sl
-rw-r--r-- 1 ja users    0 Dec 20 13:23 A
5

I don't think that's possible without -L (which gives you information on the target of the symlink instead of the symlink).

you could always run two ls invocations, one for the directories, one for the non-directories. With zsh:

lldirfirst() {
  local -a dir nondir
  local ret=0
  dir=(*(N-/)) nondir=(*(N^-/))
  (($#dir == 0)) || ls -ld -- $dir || ret=$?
  (($#nondir == 0)) || ls -ld -- $nondir || ret=$?
  return $ret
}

That's for ls -l in the current directory only, it would need to be adapted to list other files/dirs.

Also beware column alignment may get broken:

$ lldirfirst
drwxr-xr-x 2 chazelas chazelas 4096 Dec 20 13:20 c
lrwxrwxrwx 1 chazelas chazelas    1 Dec 20 13:20 d -> c
-rw-r--r-- 1 chazelas chazelas 0 Dec 20 13:20 a
-rw-r--r-- 1 chazelas chazelas 0 Dec 20 13:20 b

Another option would be to use -qF and sort the lines that end in / first (assumes GNU ls as not all ls implementation will put a / there for symlinks to directories, and a sort implementation that supports -s like GNU sort, but your --group-directories-first is GNU specific already anyway):

lsdirfirst() {
  ls -qF "$@" |
    awk '{if(/^total [0-9]+$/ || /\/$/) n=1; else n=2; print n, $0}' |
    sort -sk1,1 | cut -d' ' -f2-
}

Example:

$ lsdirfirst -l
total 4
drwxr-xr-x 2 chazelas chazelas 4096 Dec 20 13:20 c/
lrwxrwxrwx 1 chazelas chazelas    1 Dec 20 13:20 d -> c/
-rw-r--r-- 1 chazelas chazelas    0 Dec 20 13:20 a
-rw-r--r-- 1 chazelas chazelas    0 Dec 20 13:20 b

Note that with zsh, you can tell its completion system to list the directories first with:

zstyle ':completion:*' list-dirs-first true

And it does put symlinks to directories in the directory category:

$ ls Tab
Completing directory
c/  d@
Completing files
a   b

(with colors and those Completing... descriptions if enabled with more zstyles)

You can enable that completion style on a per-command basic by changing that zstyle command to:

zstyle ':completion:*:*:(ls|echo):*:*' list-dirs-first true

for instance to enable it only for the ls and echo commands.

Here's an example of ~/.zshrc configuration to get that output in color above.

eval "$(dircolors -b)"
zstyle ':completion:*' completer _expand _complete _ignored _correct _approximate _prefix
zstyle ':completion:*' format 'Completing %d'
zstyle ':completion:*' group-name ''
zstyle ':completion:*' menu select=2
zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS}
zstyle ':completion:*' list-dirs-first true
autoload -Uz compinit
compinit -i

See also the compinstall function for a menu-based interface to configure zsh completion.

2
  • IIUC, mll presented here unix.stackexchange.com/a/111639/72304 would do what OP wants in bash, right? Dec 20, 2017 at 15:12
  • If anyone uses the lsdirfirst() function and wants to use the human-readable -h option, modify the regex to read /^total [0-9\.]+\w+?$/. Without this, the "total" will display between the directories and files, which is not where it should display. Dec 26, 2018 at 20:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .