Try:
find dir1 dir2 dir3 -maxdepth 1 -type f -printf '%h\n' | awk '{c[$0]++} END{for (dir in c) printf "%6i %s\n",c[dir],dir}' | sort -n
If the directories are specified in $@
, then use:
find "$@" -maxdepth 1 -type f -printf '%h\n' | awk '{c[$0]++} END{for (dir in c) printf "%6i %s\n",c[dir],dir}' | sort -n
How it works
find dir1 dir2 dir3 -maxdepth 1 -type f -printf '%h\n'
This looks for all regular files in directories dir1, dir2, and dir3. For each file found, its directory is printed.
-maxdepth 1
(optional) tells find not to dive into subdirectories. -type f
tells find to only report on regular files. For each file found, -printf '%h\n'
tells find to print the directory that the file is in.
awk '{c[$0]++} END{for (dir in c) printf "%6i %s\n",c[dir],dir}'
This counts the number of times each directory appears on the input. After all the input has been read, it prints the totals.
We use associative array c
to count the number of times each directory is seen. In awk, $0
is the contents of the current line being read. c[$0]
is the number of times that line has been seen so far. c[$0]++
increments that count by one.
sort -n
This sorts the output in ascending order of file count. (-n
tells sort to sort numerically rather than alphabetically.)
Example
Let's suppose that we have these directories with these files:
$ ls dir{1..3}/*
dir1/a.txt dir1/c.txt dir1/e.txt dir1/f.txt dir2/b.txt dir2/d.txt dir2/f.txt dir3/b.txt
dir1/b.txt dir1/d.txt dir1/file3.txt dir2/a.txt dir2/c.txt dir2/e.txt dir3/a.txt
Our command produces the output:
$ find dir1 dir2 dir3 -maxdepth 1 -type f -printf '%h\n' | awk '{c[$0]++} END{for (dir in c) printf "%6i %s\n",c[dir],dir}' | sort -n
2 dir3
6 dir2
7 dir1
Improvement: Adding a total row
$ find dir1 dir2 dir3 -maxdepth 1 -type f -printf '%h\n' | awk '{c[$0]++} END{for (dir in c) {printf "%6i %s\n",c[dir],dir;tot+=c[dir]}; printf "%6i TOTAL",tot }' | sort -n
2 dir3
6 dir2
7 dir1
15 TOTAL
To suppress the printing of TOTAL if there is only one directory in the output:
find "$@" -maxdepth 1 -type f -printf '%h\n' | awk '{c[$0]++} END{for (dir in c) {printf "%6i %s\n",c[dir],dir;tot+=c[dir]}; if (length(c)>1)printf "%6i TOTAL",tot }' | sort -n
Include empty directories in the output
To also include empty directories:
find "$@" -maxdepth 1 -type f -printf '%h\n' | awk 'FNR==NR{c[$0]=0; next} {c[$0]++} END{for (dir in c) {printf "%6i %s\n",c[dir],dir;tot+=c[dir]}; if (length(c)>1)printf "%6i TOTAL",tot }' <(printf "%s\n" "$@") <(cat) | sort -n
As an example, let's consider an empty directory:
$ ls dir4
And, let's set $@
:
$ set -- dir4
Now, let's run our code:
$ find "$@" -maxdepth 1 -type f -printf '%h\n' | awk 'FNR==NR{c[$0]=0; next} {c[$0]++} END{for (dir in c) {printf "%6i %s\n",c[dir],dir;tot+=c[dir]}; if (length(c)>1)printf "%6i TOTAL",tot }' <(printf "%s\n" "$@") <(cat) | sort -n
0 dir4
Let's try again with two directories:
$ set -- dir1 dir4
$ find "$@" -maxdepth 1 -type f -printf '%h\n' | awk 'FNR==NR{c[$0]=0; next} {c[$0]++} END{for (dir in c) {printf "%6i %s\n",c[dir],dir;tot+=c[dir]}; if (length(c)>1)printf "%6i TOTAL",tot }' <(printf "%s\n" "$@") <(cat) | sort -n
0 dir4
7 dir1
7 TOTAL