0

How to find all folders in a directory with more than a specified number of files that are larger than a specified size?

One exemplary use-case is finding folders with split movie files which could be merged like so (ffmpeg -f concat -safe 0 -i <(for f in ./*.mp4; do echo "file '$PWD/$f'"; done) -c copy output.mp4).

2 Answers 2

3

With zsh:

x=42 size=M+1
print -rC1 -- **/*(NFe['()(($#)) $REPLY/*(N.L${size}Y${x}[$x]oN)'])

Would print the directories that contain at least 42 files whose size is strictly greater than 1MiB.

  • **/*: recursive globbing
  • (...): glob qualifiers
  • N: Nullglob: don't complain if there's no match
  • F: restrict to files of type directory that are Full (have at least one entry).
  • e['code']: filter files for which the evaluation of code returns true.
  • ()(($#)) args: anonymous function that returns true if the number of its arguments is non-zero
  • $REPLY: the file (here directory) currently being considered in code.
  • $REPLY/*: the files in there. Replace with $REPLY/**/* to also count files in subdirs.
  • .: restrict to regular files (exclude directories, symlinks, fifos, devices...).
  • LM+1: limits to files whose size rounded up to the next integer number of mebibytes is strictly greater than 1 (files with size 1048577 bytes and above).
  • Y$x: stop looking after the xth one has been found as an optimisation.
  • [$x]: select the xth in that list (again as an optimisation, so the code only needs to check it's passed at least one argument).
  • oN: don't bother sorting that list.

You can add the D qualifier to either or both globs if you also want to consider hidden dirs/files.

But if it's to do something with those files afterwards, you'd likely want to do something like:

for dir in **/*(NF); do
  large_files=($dir/*(N.LM+1))
  (( $#large_files >= 42 )) && do-something-with $large_files
done
1
  • +1 pure zsh poetry!
    – Philippos
    Jun 10, 2021 at 12:14
0

Thanks to @Muru the command, only slightly altered, is:

find . -type d -exec sh -c '[ $(find "$1" -type f -size +1M | wc -l) -gt 1 ]' _ {} \; -print

The example finds all folders in the current directory (you can replace the dot for full paths) with 2 or more files that are larger than 1 MB.

1
  • 1
    You probably want to limit the depth of the inner find's search to avoid printing the parents of the directories that contain more than n files whose size is greater than x. Also, this could be adjusted to handle newlines in file names too. For instance: find . -type d -exec sh -c '[ "$(find "$1" -path "$1/*" -prune -type f -size +1M -exec sh -c "for file; do echo .; done" innersh \{\} + | wc -l)" -gt 1 ]' outersh {} \; -print
    – fra-san
    Jun 10, 2021 at 13:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .