How to sort first directories then files etc… when using “ls” in Unix
Solution 1
The following command will list directories first, ordinary files second, and links third.
ls -la | grep "^d" && ls -la | grep "^-" && ls -la | grep "^l"
Also, it would make a great deal of sense to create an alias for this command to save keystrokes.
Edit:
If you want directories first, and then everything that is not a directory second, use this:
ls -la | grep "^d" && ls -la | grep -v "^d"
Solution 2
I do so love *nix and love seeing the inventiveness that goes into some of these replies...
Mine's not nearly as fancy on GNU Linux :
alias ls='ls --color -h --group-directories-first'
Given that I'm more comfortable with my linux CLI apps, I tend to also update coreutils on OSX :
brew install coreutils
alias ls='/usr/local/bin/gls --color -h --group-directories-first'
Solution 3
For the mac users, you can install coreutils.
This formula provides the GNU core utilities implementations, and for the commands that are also provided by macOS, they have been installed with the "g" prefix.
brew install coreutils
gls --color -h --group-directories-first
You can further simplify your life with an alias
alias ls='gls --color -h --group-directories-first'
Solution 4
There are certain things I want to see in a directory listing, and so far none of the answers here meets all of the requirements below. My requirements for a directory listing:
- Directories and files are sorted alphabetically
- Directories are listed first
- Symbolic links (symlinks) are sorted like files
- Sorting is case-insensitive
- Sorting ignores all leading non-alpha characters in a filename
- Listing includes the total count of directories (excluding
./
and../
), files, and links - Listing includes the total size (disk usage) of directories and files
- The listing has to look the same on Linux and Windows (Git Bash shell) -- this was the hardest to get right since convenient options like
--group-directories-first
don't work in Git Bash for Windows
After much hacking about, I finally came up with a one-liner (albeit a very long line ;-)) that I'm satisfied with. I have assigned this to an alias named 'dir':
ls -dlF --color * .* | head -n2 && ls -AlF | LC_ALL=C grep "^d" |
LC_ALL=C sort -k 9df && ls -AlF | LC_ALL=C grep "^[l-]" |
LC_ALL=C sort -k 9df && echo -e `find -maxdepth 1 -type d ! -name . |
wc -l` Dir\(s\) `du -hs | cut -f 1`\\t\\t`find -maxdepth 1 -type f |
wc -l` File\(s\) `find -maxdepth 1 -type f -print0 | du -ch --files0-from=- |
tail -n 1 | cut -f 1`\\t\\t`find -maxdepth 1 -type l | wc -l` Link\(s\)
To make things easier to manage, I came up with separate commands to output each segment of the directory listing to my liking, then assembled them together using the &&
operator.
ls -dlF --color * .* | head -n2
-- Extract./
and../
. We don't want to pass these throughsort
because they are already in the correct order, and sorting them can result in../
being listed first. The-d
option is to get rid of the "total" line; I like to add-F
to show the trailing slash for directories (it will also mark symlinks with "@" when you do a plainls -F
).ls -AlF | LC_ALL=C grep "^d" | LC_ALL=C sort -k 9df
-- Extract the directories and sort them by filename (9th column), ignoring both non-alpha/space characters (d
option) and character case (f
option). Thels -A
option excludes./
and../
from the listing since we already extracted them in the previous step. I tend to prefix allgrep
andsort
commands with theLC_ALL=C
locale reset so that (1) the output is consistent across Unix shells, and (2) you can sometimes see faster performance since it no longer has the overhead of the heavy UTF-8 character set to deal with.ls -AlF | LC_ALL=C grep "^[l-]" | LC_ALL=C sort -k 9df
-- This is similar to the step above, but this time we are sorting files and symlinks.find -maxdepth 1 -type d ! -name . | wc -l
-- Get the number of directories, excluding./
and../
.find -maxdepth 1 -type f | wc -l
-- Get the number of files.find -maxdepth 1 -type l | wc -l
-- Get the number of symlinks.du -hs | cut -f 1
-- Extract the total size of all subdirectories in human-readable format.find -maxdepth 1 -type f -print0 | du -ch --files0-from=- | tail -n 1 | cut -f 1
-- Extract the total size of all files in human-readable format.
Let's see our new dir
alias in action!
BEFORE:
$ ls -alF
total 22
drwxr-xr-x 13 Tom Administ 4096 Oct 25 02:38 ./
drwxr-xr-x 3 Tom Administ 0 Dec 24 2014 ../
drwxr-xr-x 15 Tom Administ 4096 Sep 17 01:23 .VirtualBox/
-rw-r--r-- 1 Tom Administ 615 Oct 25 02:38 .aliases
-rw-r--r-- 1 Tom Administ 12742 Oct 24 11:47 .bash_history
-rw-r--r-- 1 Tom Administ 3234 Oct 24 15:06 .bash_profile
drwxr-xr-x 1 Tom Administ 0 Jan 24 2015 .gem/
-rw-r--r-- 1 Tom Administ 586 Oct 24 03:53 .gitconfig
drwxr-xr-x 1 Tom Administ 4096 Dec 28 2014 .ssh/
drwxr-xr-x 4 Tom Administ 0 Jan 24 2015 .travis/
-rw-r--r-- 1 Tom Administ 6645 Oct 25 02:38 _viminfo
-rw-r--r-- 1 Tom Administ 4907 Oct 24 15:16 profile
drwxr-xr-x 1 Tom Administ 0 Oct 24 22:20 tmp/
AFTER:
$ dir
drwxr-xr-x 13 Tom Administ 4096 Oct 25 02:38 ./
drwxr-xr-x 3 Tom Administ 0 Dec 24 2014 ../
drwxr-xr-x 1 Tom Administ 0 Jan 24 2015 .gem/
drwxr-xr-x 1 Tom Administ 4096 Dec 28 2014 .ssh/
drwxr-xr-x 1 Tom Administ 0 Oct 24 22:20 tmp/
drwxr-xr-x 4 Tom Administ 0 Jan 24 2015 .travis/
drwxr-xr-x 15 Tom Administ 4096 Sep 17 01:23 .VirtualBox/
-rw-r--r-- 1 Tom Administ 615 Oct 25 02:38 .aliases
-rw-r--r-- 1 Tom Administ 12742 Oct 24 11:47 .bash_history
-rw-r--r-- 1 Tom Administ 3234 Oct 24 15:06 .bash_profile
-rw-r--r-- 1 Tom Administ 586 Oct 24 03:53 .gitconfig
-rw-r--r-- 1 Tom Administ 4907 Oct 24 15:16 profile
-rw-r--r-- 1 Tom Administ 6645 Oct 25 02:38 _viminfo
5 Dir(s) 2.8M 6 File(s) 31K 0 Link(s)
One minor downside is that you cannot have colored listings, since the color control characters surrounding the filenames make the sorting too unreliable.
UPDATE
The alias above was painfully slow when executed from the root directory of a deep file system, so I have updated to this simpler but much more performant command:
ls -AFoqv --color --group-directories-first | tail -n +2 && find -maxdepth 1 -type f -printf '%s\n' | awk '{total+=$1} END {print total" bytes"}'
Sample output:
$ dir
drwxr-xr-x 1 Tom 0 Mar 29 13:49 .aws/
drwxr-xr-x 1 Tom 0 Mar 29 13:49 .gem/
drwxr-xr-x 1 Tom 0 Mar 29 19:32 .ssh/
drwxr-xr-x 1 Tom 0 Mar 29 13:49 .zbstudio/
drwxr-xr-x 1 Tom 0 Jun 16 2016 temp/
drwxr-xr-x 1 Tom 0 Jul 13 2016 vimfiles/
-rw-r--r-- 2 Tom 365 Mar 30 10:37 .aliases
-rw-r--r-- 1 Tom 16028 Mar 30 12:12 .bash_history
-rw-r--r-- 2 Tom 2807 Mar 30 12:12 .bash_profile
-rw-r--r-- 2 Tom 2177 Mar 29 23:24 .functions
-rw-r--r-- 1 Tom 1091 Mar 30 10:34 .gitconfig
-rw-r--r-- 1 Tom 8907 Mar 29 14:45 _viminfo
-rw-r--r-- 1 Tom 2444 Jul 13 2016 _vimrc
33819 bytes
Since the new version of Git Bash for Windows supports --group-directories-first
, we no longer have to fall back on sort
. Even though the new alias doesn't display as much information as the previous alias, the performance gains are more than worth it. As a perk, you also get colors!
Solution 5
You've got several choices, depending if you want to keep alphabetical order.
You could simply try :
ls -al | sort -k1 -r
or this, to keep alphabetic order for files with the same permissions :
ls -al | sort -k1,1 -k9,9 -r
or, as eleven81 said (but this version lists everything) :
ls -la | grep "^d" && ls -la | grep "^-" && ls -al | grep -v "^[d|-]"
Related videos on Youtube
atricapilla
Updated on September 17, 2022Comments
-
atricapilla over 1 year
I would like to use the
ls
command to first show directories and then files. I tried:ls -la | sort -k 1
But I got a wrong order.
-
Julian about 14 yearsit's because
-
comes befored
when usingsort
-
JRobert almost 12 yearsOld time unix heads (the ones from the pre-GUI age) used to capitalize their folder names and make plain-file names uncapitalized to get that result automagically.
-
ippi almost 6 yearsUseful question! Perhaps it's time to change the accepted answer to one that is simpler and doesn't break terminal colors?
-
Diogo over 5 yearsTry
ls -lh --group-directories-first
-
-
eleven81 about 14 yearsThis would only work if you could be absolutely certain that every directory had no dots in its name.
-
Studer about 14 yearsThis command doesn't list everything if there are, for example, sockets or FIFOs in the folder
-
brice about 14 yearsThis is a nicer solution IMO. Using several processes and pipes to do sorting instead of piping to sort seems kind of backwards. Especially since
ls- al|sort -k1 -r
works. What was missing is just the-r
flag. -
MEM almost 12 yearsunfortunately, this doesn't work on osx terminal, since -- option is not available.
-
allyourcode almost 12 yearsre love: Is that sarcasm? If so, I'd agree.
-
Tamara Wijsman almost 12 yearsWhat do these do? Where did you learn to type them?
-
Edward J Beckett almost 12 yearsWell My preferred OS is Debian Linux. Debian's gnu core utils package version of ls supports the --show-directories-first option ... when I started using OSX I simply grabbed my all my bash dot files from my Debian box and dropped them in my home directory ... I had a lot of bash_aliases that broke so ... It was then that I had to figure out some work-arounds for my aliases ...
-
Edward J Beckett almost 12 yearsThe first one is a bit redundant ... There's really no reason to sort for the directories as it's the order we need to modify not directories ... the 'ls -la | sort -r' is what really works. Basically it's stating ... 1) A) Find . -d 1 -type d (Start from this directory, search one directory depth and search for directories only) B) ls -la (list files all attributes) C) Sort them in reverse 2) Do as 1) just drop the find ... it's not needed ... I actually like Studers' solution better ... :-)
-
Ry4an Brase over 11 yearsThat edited version should still work with other types. The first grep says everything that starts with a 'd', and the second says everything that doesn't start with a 'd'. Surely everything either starts with a d or doesn't, right?
-
Alex over 11 yearsgreat :) didn't know about the "alias" command :)
-
Mark about 11 yearsTo make the first one show other files too:
ls -la | grep "^d" && ls -la | grep "^-" && ls -la | grep -E "^d|^-" -v | grep -v "^total"
-
FCTW over 10 years@Mark -- Why not just do:
ls -la | grep "^d" && ls -la | grep "^-" && ls -la | grep -v -E "^d|^-|^total"
? -
jonathanserafini about 10 yearsRe: Doesn't work on mac ... True enough, personally though I generally do a brew install coreutils to override the mac builtins for my local user.
-
andrybak almost 10 yearsTo pipe
&&
-divided commands to less:cat <(ls -la | grep "^d") <(ls -la | grep -v "^d") | less -r
-
Kevin almost 10 yearsWhy alias
ls
? Why are we aliasing an already-existing command, and isn't typingls --group-directories-first
easy enough? -
jonathanserafini almost 10 yearsPurpose of alias ls is to define the configuration elements I want to use 90% of the time. Less keystrokes to achieve the desired output. If you ever then want to have native ls output you can always /bin/ls.
-
aliteralmind almost 9 yearsTo make @FCTW's command an OSX alias, add this to your ~/.profile:
alias la="ls -la | grep \"^d\" && ls -la | grep \"^-\" && ls -la | grep -E \"^d|^-\" -v | grep -v \"^total\""
-
FCTW almost 9 years@aliteralmind Wouldn't it just be easier to use single quotes so that you wouldn't have to escape the double-quotes?
-
Paul Irish almost 9 years
gls
rather. Right? -
benesch almost 9 yearsYes, by default. You can also use coreutils by default with
PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
. -
Six almost 9 yearsJust an fyi this doesn't appear to work. You need to use long listing format (
ls -l
) in order to filter by file type like that. Also, this command will break on spaces (in bash). You need to quote like so:"$@"
If you want to use this approach, you could do something like so:function lss {local temp="$(command ls -l --color=always "$@")"; egrep --color=never '^d|total' <<<"$temp"; egrep --color=never -v '^d|total' <<<"$temp"}
-
cde over 8 years@AndrewAshbacher no it won't. One, OSX uses bash as default most of the time. Second, it has no builtin LS. Third, it is based on BSD, not GNU/Linux. The commands above are based on the GNU Coreutils LS.
-
jonathanserafini over 8 yearsUpdated with the OSX variant with brewed coreutils
-
Ričardas Sabaliauskas over 8 years@brice The thing that bothers me about 'sort' is that
./
and../
won't be the first two lines, in that order. Other than that, I agree that it's the more efficient solution. -
Giacomo1968 over 8 yearsGreat answer! But when you say, “After much hacking about, I finally came up with a one-liner…” Yes, indeed that is not really a “one liner” in the classic sense. It might make sense to take that logic and make it a standalone shell script and just run that or call it as an alias.
-
Ričardas Sabaliauskas over 8 years@JakeGould You are fast! I haven't even finished proof-reading my answer yet and you already finished reading it and shot off a comment :). Yeah, it's a prime candidate for a function, but I was too lazy at the time, so just threw it into my .bash_aliases file. On the other hand, I usually only write a function when I cannot do something without passing parameters.
-
Tuncay Göncüoğlu over 8 yearsit might not be as fancy, but I'd prefer a command line parameter to a fancy pipe-to-single-line-script every time. No need to use a bazooka to kill a fly. Thanks :-)
-
Ryan Griggs about 8 yearsMy opinion: this should have been the accepted answer as it lists a command-line option to accomplish exactly the goal, instead of a convoluted set of grep's etc.
-
kleinfreund almost 8 yearsHow can this be modified so one can write
lsx ~/dir
that lists contents of a directory that is not the working directory? -
The Quantum Physicist over 7 yearsThis is a stupid solution. Please delete it!
-
Steven Pribilinskiy about 7 yearsI've found a working shorter notation:
ls -la|grep ^d;ls -la|grep -v ^d
(quotes aren't required and replaced&&
with;
). Another option is to introduce a variable and then evaluate it:a="ls -la|grep ^d";eval $a;eval $a -v
. Could be useful to avoid repetitions when much more options are specified tols
/grep
. There's also thatls -la --group-directories-first
option, however the shortest imo isls -la|sort
-
kleinfreund almost 7 yearsSince the commands are coupled with
&&
instead of;
, this won’t list contents of directories not containing directories. I posted an explained version as an answer for reference. -
Pirx over 6 years@10basetom: Quick question: How do you get the different colors for the columns in ls? I can color file names by filetype using ls --color, but I don't see a way to get those helpful colors for the columns. What's the secret?
-
Ričardas Sabaliauskas about 6 years@Pirx I believe the columns are colored by the default Stack Exchange syntax highlighting :-).
-
Pirx about 6 yearsYep, that seems to be the case, but it's a great idea, and solutions are in ;-)
-
Ričardas Sabaliauskas about 6 years@Pirx I suppose you can convert this into a function and try one of the answers in stackoverflow.com/questions/20151601/…
-
Cecil Curry over 5 yearsI'm in partial agreement with @TheQuantumPhysicist. Given the existence of the GNU-specific
--group-directories-first
option and the triviality of installing the GNU flavour ofls
under macOS, this is an appallingly fragile answer guaranteed to both break under common edge cases and fail to support arbitraryls
options and arguments (e.g.,--color=auto
). That said, this answer shouldn't be deleted; it should simply be ignored. Yet another quaint, overly antiquated relic of a thankfully bygone era! See this answer for a truly robust solution. -
xaa over 5 yearsnote that filenames & directory starting with uppercase will be list on top with these commands.
-
Chris almost 5 years+1 so glad I kept reading after the accepted answer
-
Danijel over 4 yearsHow to color the output?
-
jasonleonhard about 4 yearsYou might also consider adding these aliases
-
jasonleonhard about 4 years
alias folders='ls -lap | grep "/"'
-
jasonleonhard about 4 years
alias files='ls -lap | grep -v "/"'
-
mschr over 3 years+1 staying with
ls
keeps control of LS_COLORS too and thegrep
will remove color codes -
Markus Bawidamann over 3 yearsHey, so suuuuuper easy to type and very easy to remember! ;-)