10

ls option --group-directories-first causes directories to be listed on the top, which makes the output of ls nice and clean:

ls -l --group-directories-first

However, it does not act on symlinks, which are actually symlinks to directories. There is a possibility to use

ls -l -L --group-directories-first

which will list both kind of directories on top, but will not distinguish between proper directory and symlinked directory, which is again confusing.

Can ls display symlinked directories on top, while still keeping them distinct from regular directories?

EDIT: I am using bash.

4
  • This is idiosyncratic behaviour on the part of ls. According to the stat() system call, a symbolic link to a directory is still a directory (S_ISDIR(st_mode) will return true). Evidentially ls discounts the symlinks before it checks this. Commented Jan 30, 2014 at 12:05
  • 2
    @goldilocks, no, ls does lstat() (and readlink for symlinks) unless you use the -L option (in which case it uses stat()) Commented Jan 30, 2014 at 13:03
  • @StephaneChazelas : Hmm, live and learn. I had thought that S_ISLNK(st_mode) also returned true via stat(), but it doesn't -- it only does so via lstat(). Also that ISLNK doesn't return true via stat even if the link is a link to a link. Meaning that ISLNK might never returns true via stat, although that's kind of unspecified... Commented Jan 30, 2014 at 14:09
  • @goldilocks, stat gives you the properties of the file at the end of the symlink(s). If that doesn't exist or is not accessible, the stat returns a ENOENT, so what is returned by stat will never be a symlink. stat() will never tell you anything about symlinks, just like open will never open the symlink, or chmod() will not change the permissions of a symlink... etc. Commented Jan 30, 2014 at 14:27

1 Answer 1

6

No, but if using zsh, you could do:

mll() {
  (($#)) || set -- *(N-/) *(N^-/)
  (($#)) && ls -ldU -- $@
}

You could also define a globbing sort order like:

dir1st() { [[ -d $REPLY ]] && REPLY=1-$REPLY || REPLY=2-$REPLY;}

and use it like:

ls -ldU -- *(o+dir1st)

That way, you can use it for other commands than ls or with ls with different options, or for different patterns like:

ls -ldU -- .*(o+dir1st) # to list the hidden files and dirs

or:

ls -ldU -- ^*[[:lower:]]*(o+dir1st) # to list the all-uppercase files and dirs

If you have to use bash, the equivalent would be like:

mll() (
  if (($# == 0)); then
    dirs=() others=()
    shopt -s nullglob
    for f in *; do
      if [[ -d $f ]]; then
        dirs+=("$f")
      else
        others+=("$f")
      fi
    done
    set -- "${dirs[@]}" "${others[@]}"
  fi
  (($#)) && exec ls -ldU -- "$@"
)

bash doesn't have globbing qualifiers or any way to affect the sort order of globs, or any way to turn nullglob on a per-glob basis, or have local context for options (other than starting a subshell, hence the () instead of {} above) AFAIK.

1
  • 1
    Is there any similar trick for bash ? Commented Jan 30, 2014 at 11:12

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.