9

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.

  • 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. – goldilocks Jan 30 '14 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()) – Stéphane Chazelas Jan 30 '14 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... – goldilocks Jan 30 '14 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. – Stéphane Chazelas Jan 30 '14 at 14:27
5

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
    Is there any similar trick for bash ? – Martin Vegter Jan 30 '14 at 11:12

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.