184

I am able to list all the directories by

find ./ -type d

I attempted to list the contents of each directory and count the number of files in each directory by using the following command

find ./ -type d | xargs ls -l | wc -l

But this summed the total number of lines returned by

find ./ -type d | xargs ls -l

Is there a way I can count the number of files in each directory?

user784637
  • 15,392
  • 32
  • 93
  • 156
  • Are you looking for a way to count the number of files in each of the sub-directories directly under `./` ? – Tuxdude Mar 05 '13 at 05:25
  • 6
    How's this an off-topic question?? I would like to see close-voters comments with reason! **If this is off-topic then where does this belong to?** super user? I don't think so.. – Rookie Programmer Aravind Mar 05 '13 at 08:30
  • 8
    shell-script, batch-script are under programming scope! – Rookie Programmer Aravind Mar 05 '13 at 08:31
  • I was about to post Pythonic solution then I noticed that the question is closed. – anatoly techtonik Nov 14 '13 at 15:20
  • voted to reopen it. There may be other answers that could be useful in many situations (including script programming, which is the reason I reached this question). – lepe Jul 25 '14 at 08:13
  • I don't understand the off-topic vote either, unless it's because somehow shell scripting doesn't count? The "Off Topic" votes seem a bit aggressive on Stack sites sometimes. – Mark Bennett Apr 20 '15 at 16:32
  • count, sort, display in columns -> `find . -maxdepth 1 -type d -printf '%f\n' | xargs -i sh -c 'echo -n "{} " ; find {} -type f | wc -l' | sort -rnk2 | column -t` – Akhil Jan 25 '22 at 18:13

21 Answers21

323

This prints the file count per directory for the current directory level:

du -a | cut -d/ -f2 | sort | uniq -c | sort -nr
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Sebastian Piskorski
  • 4,026
  • 3
  • 23
  • 29
  • 13
    By far the best (and most elegant) solution if one wants to list the number of files in top level directories recursively. – itoctopus Apr 29 '17 at 13:41
  • 32
    This has two problems: It counts one file per directory more than there actually is and it gives a useless line containing the size of the current directory as "1 _size_". Both can be fixed with `du -a | sed '/.*\.\/.*\/.*/!d' | cut -d/ -f2 | sort | uniq -c`. Add `| sort -nr` to sort by the count instead of the directory name. – dessert Aug 04 '17 at 11:57
  • 4
    I'd like to point out that this works in OSX, too. (Just copy-pasting Linux advice into an OSX shell usually doesn't work.) – Pistos Apr 06 '18 at 16:33
  • 2
    it fetches unneeded size by du -a . Better way is using find command. but main idea is exactly the same :) – Znik Jun 20 '18 at 10:34
  • 2
    Found this one to run very slow for me – Miguel Durazo Feb 28 '19 at 22:20
  • 12
    find . -type f | cut -d/ -f2 | sort | uniq -c | sort -nr # fixes problems mentioned by dessert – jcomeau_ictx Aug 12 '19 at 07:01
  • this answer is faster https://stackoverflow.com/a/54305758/1507124 – CervEd Sep 29 '21 at 09:08
150

Assuming you have GNU find, let it find the directories and let bash do the rest:

find . -type d -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • 2
    Its just a slighly different version from the above, so: ( hint: its sorted by name and its in csv) for x in `find . -maxdepth 1 -type d | sort`; do y=`find $x | wc -l`; echo $x,$y; done – pcarvalho May 11 '13 at 17:25
  • 5
    Great one! Putting it into a single line (so it's confortable for direct usage in shell): `find . -type d -print0 | while read -d '' -r dir; do files=("$dir"/*); printf "%5d files in directory %s\n" "${#files[@]}" "$dir"; done` – lucaferrario Dec 12 '13 at 23:33
  • 17
    I needed to get the number of all files (recursively count) in each subdirectory. This modification gives you that: `find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find $dir -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done` – OmidS Dec 08 '15 at 10:21
  • @glennjackman : I guess this is not counting so called `.hidden` files. – sjsam May 10 '16 at 16:01
  • Exactly what I needed, thank you @OmidS Easy way to sort the results? – Kory Dec 20 '16 at 14:54
  • 1
    @Kory The following will do it: `find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done | sort -rn -k1` – OmidS Jan 03 '17 at 22:50
  • Can't correct it but it crashes for empty directories right ? – Fritzip Feb 27 '18 at 16:07
  • many bash implementation has a bug with loop iterations. For example if you will scan extremally large filesystem with 10-milion files, then it is possible to store many temporary objects inside bash interpreter. this cause, looping is slower step by step, and finally it will be visible like frozen proces. then generally it is not good idea using bash for looping. – Znik Jun 20 '18 at 10:31
  • for f in */; do echo "$f -> $(ls $f | wc -l)"; done – Kiran K. Oct 15 '19 at 17:27
  • 1
    @OmidS Great oneliner, but `$dir` should be inside quotes in your first comment to correctly handle dir names with whitespaces. : `find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done` – Radek Daniluk Dec 12 '19 at 07:11
  • @sjsam to also include hidden files you can use `find . -type d -print0 | while read -d '' -r dir; do files=$( ls -A "$dir" | wc -l); printf "%5d files in directory %s\n" "$files" "$dir"; done` – Lokal_Profil Feb 04 '20 at 08:09
123
find . -type f | cut -d/ -f2 | sort | uniq -c
  • find . -type f to find all items of the type file, in current folder and subfolders
  • cut -d/ -f2 to cut out their specific folder
  • sort to sort the list of foldernames
  • uniq -c to return the number of times each foldername has been counted
CervEd
  • 3,306
  • 28
  • 25
DCZ
  • 1,584
  • 1
  • 12
  • 18
21

You could arrange to find all the files, remove the file names, leaving you a line containing just the directory name for each file, and then count the number of times each directory appears:

find . -type f |
sed 's%/[^/]*$%%' |
sort |
uniq -c

The only gotcha in this is if you have any file names or directory names containing a newline character, which is fairly unlikely. If you really have to worry about newlines in file names or directory names, I suggest you find them, and fix them so they don't contain newlines (and quietly persuade the guilty party of the error of their ways).


If you're interested in the count of the files in each sub-directory of the current directory, counting any files in any sub-directories along with the files in the immediate sub-directory, then I'd adapt the sed command to print only the top-level directory:

find . -type f |
sed -e 's%^\(\./[^/]*/\).*$%\1%' -e 's%^\.\/[^/]*$%./%' |
sort |
uniq -c

The first pattern captures the start of the name, the dot, the slash, the name up to the next slash and the slash, and replaces the line with just the first part, so:

./dir1/dir2/file1

is replaced by

./dir1/

The second replace captures the files directly in the current directory; they don't have a slash at the end, and those are replace by ./. The sort and count then works on just the number of names.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    This doesn't output directory names which don't contain any files. Not sure if this is required. – Austin Phillips Mar 05 '13 at 05:51
  • True, it doesn't. 'Tis not particularly trivial to fix it to do so, since the empty directory names are not guaranteed even to appear in the output of `find`. Some might: if there's a file `dir1/dir2/dir3/file1`, but `dir1/dir2` contains only sub-directories (no plain files), then you can infer its presence. But if `dir1/dir4` has no files, it name simply doesn't appear. – Jonathan Leffler Mar 05 '13 at 06:00
  • Very useful answer if you just want to see the subdirectories of the current directory. – xixixao Oct 21 '14 at 19:09
  • Just stopped by to say thank you. 3 years after this was posted, I was looking to count 2nd level folders per folder. Your post saved me potentially many hours of tinkering with sed, find and who knows what else – Corvin Dec 29 '16 at 19:59
16

Here's one way to do it, but probably not the most efficient.

find -type d -print0 | xargs -0 -n1 bash -c 'echo -n "$1:"; ls -1 "$1" | wc -l' --

Gives output like this, with directory name followed by count of entries in that directory. Note that the output count will also include directory entries which may not be what you want.

./c/fa/l:0
./a:4
./a/c:0
./a/a:1
./a/a/b:0
Austin Phillips
  • 15,228
  • 2
  • 51
  • 50
  • It seems very expensive to run 3 commands (`bash`, `ls`, `wc`) for each directory found by `find`. – Jonathan Leffler Mar 05 '13 at 05:43
  • @JonathanLeffler Agreed, hence the first line of my answer. Your solution is better. – Austin Phillips Mar 05 '13 at 05:47
  • cool this is what i am looking for may i ask what is the '--' at the end? – once Mar 21 '16 at 02:58
  • 1
    @once The -- belongs to the bash command which will be spawned by xargs. From `man bash`, `A -- signals the end of options and disables further option processing`. In this case it would prevent a misnamed file found as part of the find from becoming part of the argument processing for bash. – Austin Phillips Mar 21 '16 at 04:15
16

Slightly modified version of Sebastian's answer using find instead of du (to exclude file-size-related overhead that du has to perform and that is never used):

 find ./ -mindepth 2 -type f | cut -d/ -f2 | sort | uniq -c | sort -nr

-mindepth 2 parameter is used to exclude files in current directory. If you remove it, you'll see a bunch of lines like the following:

  234 dir1
  123 dir2
    1 file1
    1 file2
    1 file3
      ...
    1 fileN

(much like the du-based variant does)

If you do need to count the files in current directory as well, use this enhanced version:

{ find ./ -mindepth 2 -type f | cut -d/ -f2 | sort && find ./ -maxdepth 1 -type f | cut -d/ -f1; } | uniq -c | sort -nr

The output will be like the following:

  234 dir1
  123 dir2
   42 .
Yoory N.
  • 4,881
  • 4
  • 23
  • 28
8

Everyone else's solution has one drawback or another.

find -type d -readable -exec sh -c 'printf "%s " "$1"; ls -1UA "$1" | wc -l' sh {} ';'

Explanation:

  • -type d: we're interested in directories.
  • -readable: We only want them if it's possible to list the files in them. Note that find will still emit an error when it tries to search for more directories in them, but this prevents calling -exec for them.
  • -exec sh -c BLAH sh {} ';': for each directory, run this script fragment, with $0 set to sh and $1 set to the filename.
  • printf "%s " "$1": portably and minimally print the directory name, followed by only a space, not a newline.
  • ls -1UA: list the files, one per line, in directory order (to avoid stalling the pipe), excluding only the special directories . and ..
  • wc -l: count the lines
o11c
  • 15,265
  • 4
  • 50
  • 75
  • 1
    Modification to show the file counts first on the line, and to sort by them: `find -type d -readable -exec sh -c 'ls -1UA "$1" | wc -l | tr -d "\n" ; printf "\t%s\n" "$1" ' sh {} ';' | sort -n` – Evgeni Sergeev Nov 22 '17 at 15:27
  • 1
    it executes shell many times, then it is slow and highly utilizes resources. – Znik Jun 20 '18 at 10:36
6

This can also be done with looping over ls instead of find

for f in */; do echo "$f -> $(ls $f | wc -l)"; done

Explanation:

for f in */; - loop over all directories

do echo "$f -> - print out each directory name

$(ls $f | wc -l) - call ls for this directory and count lines

Sixhobbits
  • 1,508
  • 3
  • 17
  • 26
5

This should return the directory name followed by the number of files in the directory.

findfiles() {
    echo "$1" $(find "$1" -maxdepth 1 -type f | wc -l)
}

export -f findfiles

find ./ -type d -exec bash -c 'findfiles "$0"' {} \;

Example output:

./ 6
./foo 1
./foo/bar 2
./foo/bar/bazzz 0
./foo/bar/baz 4
./src 4

The export -f is required because the -exec argument of find does not allow executing a bash function unless you invoke bash explicitly, and you need to export the function defined in the current scope to the new shell explicitly.

Tuxdude
  • 47,485
  • 15
  • 109
  • 110
  • This seems unduly complicated. It also looks to me like it gives cumulative counts for a directory hierarchy such as `./dir1/dir2/dir3` (counting the files in `dir1` and its subdirectories all together, rather than counting the files in `dir1/dir2/dir3` separately from those in `dir1/dir2` and both separately from those in `/dir1`). – Jonathan Leffler Mar 05 '13 at 05:45
  • I understood that was what the author wanted. If that not be the case, then I agree the answer is not relevant to the question. – Tuxdude Mar 05 '13 at 05:46
  • 1
    @JonathanLeffler - Okay, reading the question once again, I realized you're right - have modified the answer accordingly. – Tuxdude Mar 05 '13 at 05:52
4

My answer is a little different, due to the options of find, you can actually be much more flexible. Just try:

find . -type f -printf "%h\n" | sort | uniq -c

With the "%h" option to "-printf", find prints only the directory of the files it found. Then sort and count with "uniq -c". This prints the number of search result entries with the same directory, per directory.

Using further options on find, you can be much more flexible. For example, to get an overview how many files in which directory have been modified at a certain date, use:

find . -newermt "2022-01-01 00:00:00" -type f -printf "%TY-%Tm-%Td %h\n" | sort | uniq -c

This finds all files that have been modified since 1. January 2022, prints (with "-printf") the modification date and the directory, then sorts and counts them. In this example, each line in the result has the number of files, the date of modification (without time), and the directory.

Note that "-printf" may not be available in all versions of find I think.

André Fachat
  • 87
  • 1
  • 8
2

find . -type f -printf '%h\n' | sort | uniq -c

gives for example:

  5 .
  4 ./aln
  5 ./aln/iq
  4 ./bs
  4 ./ft
  6 ./hot
phd
  • 82,685
  • 13
  • 120
  • 165
Giddy
  • 21
  • 1
2

I combined @glenn jackman's answer and @pcarvalho's answer(in comment list, there is something wrong with pcarvalho's answer because the extra style control function of character '`'(backtick)).

My script can accept path as an augument and sort the directory list as ls -l, also it can handles the problem of "space in file name".

#!/bin/bash
OLD_IFS="$IFS"
IFS=$'\n'
for dir in $(find $1 -maxdepth 1 -type d | sort); 
do
    files=("$dir"/*)
    printf "%5d,%s\n" "${#files[@]}" "$dir"
done
FS="$OLD_IFS"

My first answer in stackoverflow, and I hope it can help someone ^_^

vacing
  • 646
  • 6
  • 9
2

THis could be another way to browse through the directory structures and provide depth results.

find . -type d  | awk '{print "echo -n \""$0"  \";ls -l "$0" | grep -v total | wc -l" }' | sh 
1

I tried with some of the others here but ended up with subfolders included in the file count when I only wanted the files. This prints ./folder/path<tab>nnn with the number of files, not including subfolders, for each subfolder in the current folder.

for d in `find . -type d -print` 
do 
  echo -e "$d\t$(find $d -maxdepth 1 -type f -print | wc -l)"
done
sthames42
  • 888
  • 8
  • 19
0

This will give the overall count.

for file in */; do echo "$file -> $(ls $file | wc -l)"; done | cut -d ' ' -f 3| py --ji -l 'numpy.sum(l)'
0

A super fast miracle command, which recursively traverses files to count the number of images in a directory and organize the output by image extension:

find . -type f | sed -e 's/.*\.//' | sort | uniq -c | sort -n | grep -Ei '(tiff|bmp|jpeg|jpg|png|gif)$'

Credits: https://unix.stackexchange.com/a/386135/354980

tsveti_iko
  • 6,834
  • 3
  • 47
  • 39
0

I edited the script in order to exclude all node_modules directories inside the analyzed one.

This can be used to check if the project number of files is exceeding the maximum number that the file watcher can handle.

find . -type d ! -path "*node_modules*" -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done

To check the maximum files that your system can watch:

cat /proc/sys/fs/inotify/max_user_watches

node_modules folder should be added to your IDE/editor excluded paths in slow systems, and the other files count shouldn't ideally exceed the maximum (which can be changed though).

funder7
  • 1,622
  • 17
  • 30
0

Easy Method:

find ./|grep "Search_file.txt" |cut -d"/" -f2|sort |uniq -c

0

In my case I needed the count at subfolder level, so I did:

du -a | cut -d/ -f3 | sort | uniq -c | sort -nr
Adriaan
  • 17,741
  • 7
  • 42
  • 75
  • So what you are saying is "I had a slightly different problem, which I found a solution for in the answers here, but I do not provide any new insight on the problem described at the top of this page". Any mixture of "Thanks", "Me too" or "I have a different problem, with a solution" is not considered an answer here. Please either [edit] according to [answer], and for the question at the top of this page - or delete this. – Yunnosch Jan 12 '23 at 10:52
  • As already mentioned, this isn't an answer to the problem listed. However, you could make this an answer by specifying what changes you made and what they do. Idealy, you'd show an answer to the original question AND what changes you made for your usecase. – James Jan 12 '23 at 18:00
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/33615884) – rastasheep Jan 18 '23 at 09:50
-1

Easy way to recursively find files of a given type. In this case, .jpg files for all folders in current directory:

find . -name *.jpg -print | wc -l

RexBarker
  • 1,456
  • 16
  • 14
-2

omg why the complex commands. just use something like

find whatever_folder | wc -l
Dimitar Marinov
  • 922
  • 6
  • 14
  • Because this does not produce the desired output. The question clearly asks for files in EACH directory, not the sum of all of them. – DreamWave Nov 26 '21 at 09:17