I have lots and lots of files in a series of directories, and would like a nicely formatted list of how many files are in each directory.

The directories are ordered thus: Parent directories named A, B, ... H, I. Under each of these, there are from 1 to 7 directories named 001, 002, ... 007. In these directories, the files I'd like a count of files.

What I'm hoping for, is something like this:

C/003    122
C/004     45
C/005    462
D/001    215
E/001     98
E/002    323

I'm thinking a tab between directory-name and count. It doesn't matter if the count is left or right justified.

What I've done myself is basically this:

( for i in */*
  echo ">>> $i"
  ls "$i" | wc
done ) > file_count

The result is something like this:

>>> F/002
  30   30   234
>>> F/003
  120  120  1322
>>> G/001
  78   78   620

Obviously, not bad... and if I replaced wc with wc -l and removed the ">>>" it would be even better - except that each "record" would be over two lines rather than one.

So what I need, is some awk or sed (or whatever - is there a simpler tool for joining two lines than those two?) magic to join the two-and-two lines into one.

However, if there is a more direct approach (shell-script, perl, python, some really dark sed & awk magic) that could make a list in "one go", that would be even better...

5 Answers 5


Assuming your file names don't contain newline characters and your grep supports the -o option:

find [[:upper:]] -type f | grep -Eo '^./[0-9]{3}' | sort | uniq -c
  • 1
    FYI this guy discovered the shellshock bug in bash
    – Punit Arya
    Commented Aug 28, 2015 at 20:17

You can built an array of files for each directory separately, and then just count the number of elements. In bash that would be something like

for dir in */*/; do a=( "$dir"/* ); printf "%s\t%s\n" "$dir:" "${#a[@]}"; done

If A/001 etc. contains directories too which content you would like to include, then add ** glob:

shopt -s globstar
for dir in */*/; do a=( "$dir"/**/* ); printf "%s\t%s\n" "$dir:" "${#a[@]}"; done

The simplest way to get what you want is to use the -n option to echo when you print the directory name. That avoids printing a newline at the end of whatever you're echoing, so the next output stays on the same line. Other languages may be a better option if you want to collect the information you're looking for and then run multiple transformations on it before giving output.


This will do what you've asked:

for d in */*
    n=$(find "$d" -maxdepth 1 -type f -printf "ok\n" | wc -l)
    printf "%s\t%d\n" "$d" $n

The primary differences to your code are that I've used find instead of ls so that weirdly named files won't break the count (think of \n in a filename), and that I've used printf to format the output.


Perhaps something like:

find $PWD/ -type f -printf "%h\n\0" | uniq -zc

Yield something like:

  7 /foo
 17 /foo/bar
  9 /foo/baz

Add a | sort -z before | uniq to sort it.

You must log in to answer this question.

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