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?

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
4

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.

  • IIUC, mll presented here unix.stackexchange.com/a/111639/72304 would do what OP wants in bash, right? – Arkadiusz Drabczyk Dec 20 '17 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. – danemacmillan Dec 26 '18 at 20:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.