Was hoping I would be able to do this with the find command but I can see no test in the manual for doing what I want. I would like to be able to find any directories in the working directory that contain less-than, more-than or exactly the count I specify.

find . -filecount +10 # any directory with more than 10 entries
find . -filecount 20 # any directory with exactly 20 entries

But alas there is no such option.

  • try something like "ls -al | wc -l | grep"
    – Vanadis
    Commented Jul 8, 2013 at 17:13

7 Answers 7


You could try this, to get the sub directory names and the number of files/directories they contain:

find . -maxdepth 1 -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \;

If you want to do the same for all sub directories (recursive find) use this instead:

find . -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \;

To select those directories that have exactly 10 files:

find . -maxdepth 1 -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \; | 
  awk '$NF==10'

10 or more:

find . -maxdepth 1 -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \; | 
 awk '$NF>=10'

10 or less:

find . -maxdepth 1 -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \; | 
 awk '$NF<=10'

If you want to keep only the directory name (for example of you want to pipe it to another process downstream as @evilsoup suggested) you can use this:

find . -maxdepth 1 -type d -exec bash -c "echo -ne '{}\t'; ls '{}' | wc -l" \; | 
 awk -F"\t" '$NF<=10{print $1}'
  • 1
    I think it might be useful to include the awk command to cut off the file count (i.e. the last whitespace-delimited column), in case the questioner wants to pipe the output to something else.
    – evilsoup
    Commented Jul 8, 2013 at 20:09
  • 1
    @evilsoup good idea, done.
    – terdon
    Commented Jul 8, 2013 at 20:51
  • To support white spaces and special characters in the directory names; try reversing the usage of single- and double quotes as such: find . -type d -exec bash -c 'echo -ne "{} "; ls "{}" | wc -l' \; | awk '$NF<=10' Commented Sep 22, 2018 at 17:18
  • 1
    Here's a slightly shorter production that only uses find and bash (doesn't need ls, wc and awk): find . -type d -exec /bin/bash -c 'd="{}"; a=( "${d%/*}"/* ); [[ ${#a[*]} == 10 ]]' ';' -print
    – eatnumber1
    Commented Oct 8, 2020 at 20:46
  • @RussellHarmon that deserves its own answer. Unlike mine, yours can deal with file names containing newlines, so it isn't just a small improvement: you're going from a limited solution that works on some files (mine) to one that works on arbitrary file names (yours). Post it as an answer!
    – terdon
    Commented Oct 8, 2020 at 22:17

Terdon's answer is generally well illustrative of the right approach, however here's a slightly shorter answer which only uses find and bash (doesn't need ls, wc and awk), and can also deal with files or directories containing newlines.

find . -type d -exec /bin/bash -c 'a=( "{}"/* ); [[ ${#a[*]} == 10 ]]' ';' -print

  • 2
    (For my future reference) This iterates through the directories found with find and for each sets the a variable to an array of the contents of the directory with a=( "{}"/* ). Then the number of elements in the array, ${#a[*]} is compared against 10. If the number of elements is indeed 10 then the -exec command returns true (due to the 0 exit code from bash). Since -exec returns true, the -print argument is executed and the directory name is printed indicating that the directories listed have exactly 10 files/directories inside them.
    – gene_wood
    Commented Nov 24, 2021 at 5:56
  • This solution treats directories like files. In other words if finds directories containing 10 files or subdirectories.
    – sfera
    Commented Dec 23, 2022 at 22:13

To list immediate subdirectories containing exactly $NUM files.

find -maxdepth 2 -mindepth 2 -type f -printf '%h\0' | awk -v num="$NUM" 'BEGIN{RS="\0"} {array[$0]++} END{for (line in array) if (array[line]==num) printf "%s\n", line}'

To list immediate subdirectories containing greater than $NUM files.

find -maxdepth 2 -mindepth 2 -type f -printf '%h\0' | awk -v num="$NUM" 'BEGIN{RS="\0"} {array[$0]++} END{for (line in array) if (array[line]>num) printf "%s\n", line}'

To list immediate subdirectories containing less than $NUM files.

find -maxdepth 2 -mindepth 2 -type f -printf '%h\0' | awk -v num="$NUM" 'BEGIN{RS="\0"} {array[$0]++} END{for (line in array) if (array[line]<num) printf "%s\n", line}'

Items are terminated by a null character \0, so file names that contain newlines or other types of white space will be interpreted correctly. The %h prints each file's dirname. awk then uses an array to count how many times it encounters each directory, printing it if the conditions are met.

Please note that none of the aforementioned commands will display directories containing zero files. Also note that by file I am referring to regular files, not links, directories, sockets, blocks, named pipes, etcetera.

I've tried to do this as simply as possible. If you want to find recursive subdirectories or the files therein, a modified command is required. There are too many possibilities to list them all.


Try this:

[ `find . | wc -l` -eq 10 ] && echo "Found"

[ `find . | wc -l` -gt 10 ] && echo "Found"

[ `find . | wc -l` -lt 10 ] && echo "Found"

In this examples you can check if CURRENT directory contains exactly 10, more then 10 and less then 10 files/directories. If you need to check bunch of directories, just use loop.

  • Your solution also counts the current directory (.), you may want to modify accordingly.
    – terdon
    Commented Jul 8, 2013 at 18:38
  • I like the thrust of this answer (because I'm a glutton for doing things in the shell), but you'd be better off using wc -l < <(printf %s\\n ./*) or printf %s\\n ./* | wc -l inside the test, to avoid an unnecessary find call. This will also avoid the problem that @terdon noted, of including . in the result. However, it would also run into the problem of ignoring files beginning with a .; I would solve this with shopt -s dotglob (to make globs match files beginning with a ., but not . or ..).
    – evilsoup
    Commented Jul 8, 2013 at 20:23
  • @terdon It is not important. It is not final solution, just example, idea. You can -1, or change 10 to 11 in final version.
    – september
    Commented Jul 8, 2013 at 21:49
  • I know, and the idea is good that's why I made the suggestion.
    – terdon
    Commented Jul 8, 2013 at 22:02
  • @ terdon. Thank you. There can be many different requirements, like: Count only files but not not directories, or links, or hard links. Count or not files in subdirectories. Count hidden files (like .bashrc)... ...so your expression can be veeeeery loooong. :)
    – september
    Commented Jul 8, 2013 at 22:16

For finding dirs with exactly 20 files with greedy grep

find -type f | grep -o '.*\/' | sort | uniq -c | grep -e '^\s+20'

If you want to get the number of all files - also those nested in subdirectories - contained in directories up to a specific depth, you can extend terdons answer with another call to find. This worked for my case:

find . -mindepth 1 -maxdepth 1 -type d -exec bash -c "echo -ne '{} '; find '{}' -type f | wc -l" \; | awk '$NF>3'

What I like about this approach is that with the second call to find the expression becomes highly customizable (by adding -name "..." flag or -maxdepth -mindepth an so on)


I had some large (>100K files) cases so I played around with this a bit.

eatnumber1's [answer][https://superuser.com/a/1592155/1776699] is nice but can be further improved to reduce the number of shell processes created by using the '+' option to 'find -exec'.

Parameterizing by the desired size=10, this had the best performance: find $* -type d -exec /bin/bash -c 'for f do a=( "$f"/* ); [[ ${#a[*]} == '$size' ]] && echo "$f"; done; true' exec-sh {} + This is at least 4x faster on some large test cases, up to 12x.

Note that by default the globbing solution will ignore hidden files, but you can use shopt to alter tihs behavior: find . -type d -exec /bin/bash -c 'shopt -s nullglob dotglob; for f do a=( "$f"/* ); [[ ${#a[*]} == '$size' ]] && echo "$f"; done; true' exec-sh {} + Alternatives using using 'ls -fA "$f"' instead of globbing were slower than eatnumber1's solution.

I also explored using a pipeline to pass the directory names to the shell case, and found this to have similar performance: find . -type d -print | /bin/bash -c 'shopt -s nullglob dotglob; while IFS= read -r f; do a=( "$f"/* ); [[ ${#a[*]} == '$size' ]] && echo "$f"; done; true'

Using gnu parallel for the pipeline was more expensive for my use cases, but might be useful in other cases: find . -type d -print | parallel -q /bin/bash -c 'shopt -s nullglob dotglob; a=( "{}"/* ); [[ ${#a[*]} == '$size' ]] && echo "{}"; true'


You must log in to answer this question.

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