I'm looking for a script that lists all files in a directory and subdirectories sorted by size, listing only file names not complete paths.

find . -type f -exec ls -lhS {} \+ works but gives the path.

find . -type f -printf '%f\n' also works but I'm not sure how to sort it or if it's possible to include the file sizes here as well.

  • You can pipe output to grep find . -type f -exec ls -S {} \+ | grep -o "[^/]*$" – Costas Jan 29 '15 at 0:34
  • Actually I'm not sure right now - do you want to remove only leading path from the result (the same string for all files) or entire path for all files (including subdirectories) so that you will have only basename of the files? Also what is desired format of the output - your two commands gives completely different results. – jimmij Jan 29 '15 at 2:19

If your find supports it, you can use %s in -printf ("File's size in bytes").

If your sort supports nul-delimited input (-z), you can then do:

find . -type f -printf "%s %f\0" | sort -nz | tr '\0' '\n'

If your system supports zsh command, then in bash script you can run

zsh -c 'ls -lhS -- **/*(.D)'

This probably requires some explanation:

  • zsh: other than bash, more powerful shell with a lot of features
  • -c: take next argument as a command to execute by zsh
  • ls -lhS: according to your question this is the command you want to execute
  • --: takes care of strange filenames which may start with - character
  • **: glob matching over multiple directories
  • *: standard glob - matching all files in those directories
  • (): glob qualifiers specifying which files and in which order should be selected
  • .: we select only plain files (equivalent of type f in find)
  • D: include dot-files (hidden files) in the result

After re-reading the question I suppose you rather want to delete the entire path from the result, not only leading part. This still can be done with zsh:

print -l **/*(.OLDe:'reply=${REPLY##*/}':)

but that may be little too much as this is not question about zsh tricks after all, so I will propose "standard" solution with find + sed:

find . -type f -exec sh -c 'ls -lhS "$@" | sed "s| \..*/| |"' {} \+

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.