21

I would like a BASH command to list just the count of files in each subdirectory of a directory.

E.g. in directory /tmp there are dir1, dir2, ... I'd like to see :

`dir1` : x files 
`dir2` : x files ...
31

Assuming you want a recursive count of files only, not directories and other types, something like this should work:

find . -maxdepth 1 -mindepth 1 -type d | while read dir; do
  printf "%-25.25s : " "$dir"
  find "$dir" -type f | wc -l
done
|improve this answer|||||
  • Also, I get "find: warning: you have specified the -maxdepth option after a non-option argument -type, but options are not positional (-maxdepth affects tests specified before it as well as those specified after it). Please specify options before other arguments." – jldupont Sep 14 '12 at 21:38
  • 2
    Both answers given so far will give incorrect results in the unlikely case that there are files whose names include newline characters. You can handle that with a find ... -print0 | xargs -0 .... – Scott Sep 14 '12 at 21:57
  • @jldupont: move the depth arguments before the ´-type d´, I've edited the answer. – Thor Sep 14 '12 at 22:58
  • Yes, and let me add the information that this excellent solution will not take any external variables and thus will work with bash alias!! – syntaxerror Nov 25 '14 at 13:07
  • fast, and piping sort -rn -k 2,2 -t$':' you get the DESC list – Andre Figueiredo Apr 18 '18 at 23:05
14

This task fascinated me so much that I wanted to figure out a solution myself. It doesn't even take a while loop and MAY be faster in execution speed. Needless to say, Thor's efforts helped me a lot to understand things in detail.

So here's mine:

find . -maxdepth 1 -mindepth 1 -type d -exec sh -c 'echo "{} : $(find "{}" -type f | wc -l)" file\(s\)' \;

It looks modest for a reason, for it's way more powerful than it looks. :-)

However, should you intend to include this into your .bash_aliases file, it must look like this:

alias somealias='find . -maxdepth 1 -mindepth 1 -type d -exec sh -c '\''echo "{} : $(find "{}" -type f | wc -l)" file\(s\)'\'' \;'

Note the very tricky handling of nested single quotes. And no, it is not possible to use double quotes for the sh -c argument.

|improve this answer|||||
  • It is slower as it invokes /bin/sh for each directory. You can check this with strace -fc script. Your version makes about 70% more system-calls. +1 for shorter code :-) – Thor Oct 31 '16 at 13:58
  • 1
    inspired by this; sorted by filecount: find . -maxdepth 1 -mindepth 1 -type d -exec sh -c 'echo "$(find "{}" -type f | wc -l)" {}' \; | sort -nr – mnagel Mar 21 '17 at 8:44
7
find . -type f | cut -d"/" -f2 | uniq -c

Lists a folders and files in the current folder with a count of files found beneath. Quick and useful IMO. (files show with count 1).

|improve this answer|||||
  • 1
    How about a little explanation of how it is working ? :) – C0deDaedalus Oct 4 '18 at 13:32
  • 1
    awesome, thanks! You might want to add | sort -rn to sort subdirs by number of files. – Dennis Golomazov Sep 18 '19 at 8:12
1

Using find is definitely the way to go if you want to count recursively, but if you just want a count of the files directly under a certain directory:

ls dir1 | wc -l

|improve this answer|||||
  • I don't want to do this for each of the 1000's of directories I've got there... – jldupont Sep 14 '12 at 21:37
  • Then use xargs. ls -d */ | xargs -n1 ls | wc -l (Use the answer you accepted if it already works, though! This is just And Now You Know.) – jrajav Sep 14 '12 at 21:41
  • your proposal didn't show up any results in many seconds whereas the answer I accepted did. – jldupont Sep 15 '12 at 8:03
  • @jrajav this approach absolutely fails for directories with whitespace in them. This is why find is so important. (let alone -print0 and xargs -0, already pointed out by Scott in the other answer) – syntaxerror Nov 25 '14 at 13:03
1
find . -mindepth 1 -type d -print0 | xargs -0 -I{} sh -c 'printf "%4d : %s\n" "$(find {} -type f | wc -l)" "{}"'

I need often need to count the number of files in my sub-directories and use this command. I prefer the count to appear first.

|improve this answer|||||
0

What I use... This makes an array of all the subdirectories in the one you give as a parameter. Print the subdirectory and the count of that same subdirectory until all the subdirectories are processed.

#!/bin/bash    
directories=($(/bin/ls -l $1 | /bin/grep "^d" | /usr/bin/awk -F" " '{print $9}'))

for item in ${directories[*]}
    do
        if [ -d "$1$item" ]; then
            echo "$1$item"
            /bin/ls $1$item | /usr/bin/wc -l
        fi
    done
|improve this answer|||||
0

You could use this python code. Boot up the interpreter by running python3 and paste this:

folder_path = '.'
import os, glob
for folder in sorted(glob.glob('{}/*'.format(folder_path))):
    print('{:}: {:>8,}'.format(os.path.split(folder)[-1], len(glob.glob('{}/*'.format(folder)))))

Or a recursive version for nested counts:

import os, glob
def nested_count(folder_path, level=0):
    for folder in sorted(glob.glob('{}/'.format(os.path.join(folder_path, '*')))):
        print('{:}{:}: {:,}'.format('    '*level, os.path.split(os.path.split(folder)[-2])[-1], len(glob.glob(os.path.join(folder, '*')))))
        nested_count(folder, level+1)
nested_count('.')

Example output:

>>> figures: 5
>>> misc: 1
>>> notebooks: 5
>>>     archive: 65
>>>     html: 12
>>>     py: 12
>>>     src: 14
>>> reports: 1
>>>     content: 6
>>> src: 1
>>>     html_download: 1
|improve this answer|||||

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.