I'm looking to list the entire contents of a directory, including the contents of subfolders but sorted by filesize.
Thus far I've managed to get as far as listing and sorting whilst still being recursive with ls -lhSR
(the h
is nice to have but definitely not essential for me, as long as I can get file sizes).
I am likely overlooking something obvious, or asking the impossible, but any advice here would be greatly appreciated.
5 Answers
You can use find:
find . -type f -printf "%s %P\n" | sort -n
Optional: To convert byte values to human-readable format, add this:
| numfmt --to=iec-i --field=1
Explanation:
find in current directory (.) all files (-type f)
-printf: suppress normal output and print the following:
%s - size in bytes
%P - path to file
\n - new line
| sort -n: sort the result (-n = numeric)
-
Oh perfect! Thank you so much, this did exactly what I needed!– user806346Jun 14, 2018 at 14:49
-
3I'm glad my answer helped you. Please consider clicking the checkmark on the left side to mark the answer accepted. Thanks.– pLumoJun 14, 2018 at 14:58
-
1@RoVo it would also be nice to upvote the question since, given that you've answered it, you probably found it interesting and useful.– terdonJun 14, 2018 at 15:12
-
-
@toms It's OK to wait a while (maybe a day or so) to accept the answer, even when it's as good as this one. Once the answer is accepted, there's no way for another answer to turn out to be even better. And because of that, a lot of people won't bother to submit any other answers, so we don't get a chance to see them to find out if one's better. Jun 14, 2018 at 16:51
Since you didn't specify a particular shell, here's an alternative using zsh's glob qualifiers with
setopt extendedglob
for the recursion. Then for example:
recursively list plain files:
printf '%s\n' **/*(.)
recursively list plain files, ordered by increasing Length (i.e. size):
printf '%s\n' **/*(.oL)
recursively list plain files, Ordered by decreasing size:
printf '%s\n' **/*(.OL)
recursively list plain files, ordered by decreasing size, and select the top 3 results:
printf '%s\n' **/*(.OL[1,3])
If you want the file sizes as well, then you could use
du -hb **/*(.OL[1,3])
With the globstar
shell option set you can use shell globbing:
shopt -s globstar # don’t match hidden files
shopt -s globstar dotglob # match hidden files
stat -c"%s %n" **/* | sort -n
If you try that with too many files, you‘ll get an “Argument list too long” error. To work around that, you can use printf
and xargs
:
printf "%s\0" **/* | xargs -0 stat -c"%s %n" | sort -n
I just realized this prints the directories (with a size of 4096 bytes) as well – if you don’t want that, use this instead:
stat -c"%A %s %n" **/* | sed '/^d/d;s/\S* //' | sort -n
printf "%s\0" **/* | xargs -0 stat -c"%A %s %n" | sed '/^d/d;s/\S* //' | sort -n
Example run
$ tree
.
├── edits.png
├── makescript
├── new
│ └── edits.png
└── test
└── 1.png
2 directories, 4 files
$ stat -c"%s %n" **/* | sort -n
0 test/1.png
43 makescript
2160 edits.png
2160 new/edits.png
4096 new
4096 test
$ stat -c"%A %s %n" **/* | sed '/^d/d;s/\S* //' | sort -n
0 test/1.png
43 makescript
2160 edits.png
2160 new/edits.png
-
Nice solution. Compared to find, it doesn't include hidden files, how to achieve that?– pLumoJun 15, 2018 at 8:47
-
@RoVo Always forget about these – you just need to set the
dotglob
shell option, see my updated answer.– dessertJun 15, 2018 at 9:41 -
Instead of stripping directories after the fact with sed, you could consider something like
printf "%s\0" **/* | xargs -0 sh -c 'for f; do [ -d "$f" ] || stat -c "%s %n" "$f"; done' sh | sort -n
Jun 15, 2018 at 12:43 -
You can use
ls -lhSd **/*
if you don't mind having the directories as part of the list. Or if none of your directory names have.
in them, and all the files you want do, you canll -hS **/*.*
, or similar. Jun 15, 2018 at 12:57 -
If you don't have zsh, you can still use du
+ sort
:
Human-readable sizes, including cumulative sizes of directories:
du --apparent-size -ah0 . | sort -zh | xargs -0L1
Only files (using
find
):find . -type f -print0 | du --files0-from=- --apparent-size -ah0 | sort -zh | xargs -0L1
In both cases, I have opted to use null-terminated lines (-0
, -z
, -print0
options), to be safe against all valid filenames.
For quick interactive use on directory trees that aren't too huge, shopt -s globstar
is really nice. A glob can't filter out directories based on type, but if you use it with ls -d
then ls
will just print the directory name, instead of the contents.
Assuming your ll
alias includes -lh
:
# with shopt -s globstar in your .bashrc
ll -rSd **/*
will give you output like this (from my code-golf directory), but with colour highlighting (so it's easier to see the directories). Note that sorting by filesize happened across subdirectories.
drwxr-xr-x 1 peter peter 70 Jun 8 07:56 casexchg
...
drwxr-xr-x 1 peter peter 342 Mar 13 18:47 parity-party
-rw-r--r-- 1 peter peter 387 Jul 29 2017 likely.cpp
-rw-r--r-- 1 peter peter 416 Aug 31 2017 true-binary.asm~
-rw-r--r-- 1 peter peter 447 Feb 23 20:14 weight-of-zero.asm
...
-rw-r--r-- 1 peter peter 6.4K Jun 1 2017 string-exponential.asm
-rwxr-xr-x 1 peter peter 6.7K Aug 31 2017 true-binary
-rwxr-xr-x 1 peter peter 6.8K Sep 17 2017 dizzy-integer
-rw-r--r-- 1 peter peter 7.5K Jul 24 2017 fibonacci/fibonacci-1G.v3-working-32b-stack-except-output.asm
-rw-r--r-- 1 peter peter 8.4K Jul 25 2017 fibonacci/perf.32bit-pop-114limb.sub-cmc.1G~
-rw-r--r-- 1 peter peter 8.4K Jul 25 2017 fibonacci/perf.32bit-pop-114limb.sub-cmc.1G
-rwxr-xr-x 1 peter peter 8.4K May 19 04:29 a.out
-rw-r--r-- 1 peter peter 8.9K Jul 25 2017 fibonacci/perf.python-xnor-2n
-rw-r--r-- 1 peter peter 9.5K Jul 26 2017 fibonacci/fibonacci-1G-performance.asm
-rwxr-xr-x 1 peter peter 9.6K Apr 12 23:25 empty-args
-rw-r--r-- 1 peter peter 9.7K Dec 18 17:00 bubblesort.asm
-rwxr-xr-x 1 peter peter 9.9K Feb 6 23:34 parity-party/a.out
-rw-r--r-- 1 peter peter 9.9K Jul 25 2017 fibonacci/fibonacci-1G-performance.asm~
...
You could filter out the directories by piping through grep -v '^d'
You can sometimes use a glob that matches only files and not directories, if your filenames have a pattern. e.g. ll -rSd **/*.jpg
, or even **/*.*
works if none of your directory names have .
in them, and all the files you want do.
(For people with a DOS background: there's nothing magical about *.*
on Unix. It just matches any directory entry that contains a literal dot. But other than executables and sometimes text files, it's common to give extensions to filenames.)
@dessert points out you would need shopt -s dotglob
for it to match all files.
With GNU find
If there aren't too many files to fit on one ls
command line, find -exec ls {} +
will put them all on on command line where ls
can sort them.
find -not -type d -exec ls --color -lrSh {} +
Using -not -type d
instead of -type f
avoids ignoring symlinks, named pipes, sockets, device files, and whatever else you have kicking around in your directories.
With du
:
du -ach | sort -h
....
4.0K x86-modedetect-polyglot.o
8.0K ascii-compress-base.asm
8.0K dizzy-integer
8.0K stopwatch-rdtsc.asm
8.0K string-exponential.asm
8.0K true-binary
12K a.out
12K bubblesort.asm
12K casexchg
12K empty-args
100K parity-party
220K fibonacci
628K total
Now directory names are sorted into the list with to sum total of all their contents, but individual files are still included.
sort -h
, aka --human-numeric-sort
, sorts numbers with size suffixes like du -h
prints. It's perfect for use with du
.
I often use du -sch * | sort -h
, or */
to get only directories.
du -sch **/* | sort -h
would give you the above output, if you forget that du
has a -a
option.
(I only took the time to look it up because I'm posting an answer. For interactive use, I probably would have just used du -sch **/*
.