find . -type f | wc -l counts every file under the current directory, including subdirectories, and excluding the directories themselves. The interesting numbers are usually the breakdowns — by extension and by subfolder — and there is one case where wc -l lies to you: a filename containing a newline.
1. How many files in total
$ clai how many files are in this folder and all subfolders→ find . -type f | wc -l20
-type f leaves out directories and symlinks, so the number means something. ls | wc -l in the same folder answers 3 — it only sees the top level and counts directories as entries.
2. Break it down by extension
$ clai count the files by extension→ find . -type f -name '*.*' | sed 's/.*\.//' | sort | uniq -c | sort -rn 7 jpg 6 py 4 md 1 txt 1 png 1 bin
This is usually the question behind the question: what is this directory actually made of. -name '*.*' drops files with no extension — without it sed hands you the whole filename instead.
3. Break it down by subfolder
$ clai count files in each subfolder→ for d in */; do printf '%5s %s\n' "$(find "$d" -type f | wc -l)" "$d"; done 3 docs/ 8 img/ 7 src/
A loop over the top-level directories, counting recursively inside each. Hidden directories don't match */, which is why these numbers add up to less than the total.
4. Without hidden files
$ clai count files but skip hidden ones→ find . -type f -not -path '*/.*' | wc -l18
20 versus 18 — exactly the two dotfiles. -not -path '*/.*' also excludes everything inside hidden directories like .git, which is usually what you meant.
5. The trap: a newline in a filename
$ clai count files reliably even if names contain newlines→ find . -type f -printf '.' | wc -c21
Tested with a file whose name spans two lines: find . -type f | wc -l reports 22 in the same directory. wc -l counts lines, not files. -printf '.' emits one dot per file and the count is right again. The portable version without GNU extensions is find . -type f -print0 | tr -dc '\0' | wc -c.
6. Files and directories in one line
$ clai show how many files and how many directories are here→ printf 'files: %s\ndirs: %s\n' "$(find . -type f | wc -l)" "$(find . -type d | wc -l)"
-type d counts the current directory too, so the directory number is always one higher than you expect.
Gotchas
ls | wc -lcounts the wrong thing. It never enters subdirectories, it does count directories, and it skips dotfiles.find -type fis the one that answers the question you asked.wc -lbreaks on newlines in filenames. Verified: 21 files, answer 22. When the directory came from somewhere else — users, an archive, a scraper — count with-printf '.'or-print0.find .walks into hidden directories. In a repository.gitalone can add thousands of files to your total. Filter with-not -path '*/.*'or the number means nothing.
Related questions
How do I count only the top level? find . -maxdepth 1 -type f | wc -l stays out of subdirectories.
How do I count one file type? find . -type f -name '*.log' | wc -l. Case matters; use -iname when it shouldn't.
What if I want size instead of count? du -sh . gives the total size, and du -h --max-depth=1 | sort -h breaks it down per subfolder.
See also
CliAI turns "how many files by type" into the whole pipeline, and shows it before it runs. Install it in one line.