How to use wc and piping to find how many files and directories are in a certain directory?
One approach would be to make use of ls
to give us a list of the files, but we want this list to be guaranteed to show only 1 file or directory per line. The -1
switch will do this for us.
$ ls -1
dir1
dir2
dir3
fileA
fileB
fileC
Example
Create the above sample data in an empty directory.
$ mkdir dir{1..3}
$ touch file{A..C}
Check it:
$ ls
dir1 dir2 dir3 fileA fileB fileC
Now to count you can use wc -l
to count the number of lines, which correspond to a file or directory in the ls -1
output.
$ ls -1 | wc -l
6
(note however that it doesn't include the hidden files)
Counting files or directories, just not together
To count either files or directories you need to change your tactic slightly. In this case I'd use ls -l
since it shows what's a directory and what's a a file.
Example
$ ls -l
total 12
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir1
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir2
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir3
-rw-rw-r-- 1 saml saml 0 Nov 16 09:49 fileA
-rw-rw-r-- 1 saml saml 0 Nov 16 09:49 fileB
-rw-rw-r-- 1 saml saml 0 Nov 16 09:49 fileC
Then we can use grep
to filter out directories or not-directories like so:
# directories
$ ls -l | grep "^d"
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir1
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir2
drwxrwxr-x 2 saml saml 4096 Nov 16 09:48 dir3
# regular files
$ ls -l | grep "^-"
-rw-rw-r-- 1 saml saml 0 Nov 16 09:49 fileA
-rw-rw-r-- 1 saml saml 0 Nov 16 09:49 fileB
-rw-rw-r-- 1 saml saml 0 Nov 16 09:49 fileC
Now just use wc -l
again to count the above:
# directories
$ ls -l | grep "^d" | wc -l
3
# regular files
$ ls -l | grep "^-" | wc -l
3
Though, you can avoid wc
altogether, and use grep
's -c
option:
$ ls -l | grep -c '^d'
(again, hidden files are not included. Note that directories and regular are two types of files. There are many more like named pipes, symbolic links, devices, sockets...).
Recursion
If you need to find the files and directories recursively under /usr/bin
then you'll likely want to change tactics entirely and make use of another tool called find
.
Example
$ find /usr/bin | wc -l
4632
(though above /usr/bin
itself is included in the count)
The same techniques I used above could be employed use ls
to do something similar but ls
is generally not a good tool to parse the output. find
on the other hand was built for this, and offers switches to find either files or directories.
# find files
$ find /usr/bin -type f
# find directories
$ find /usr/bin -type d
(note that this time, find
is including hidden files (except .
and ..
)).
newlines?
I've never figured out why a newline character is a legal character to use when creating file names or directory names. So the methods discussed above using wc
and ls
would not contend with these, so use them with that in mind.
Example
Create a directory & file name with newlines.
$ mkdir $'dir4\n5'
$ touch $'fileD\nE'
ls
shows them correctly:
$ ls -1
dir1
dir2
dir3
dir4?5
fileA
fileB
fileC
fileD?E
But wc
counts the directories and files that contain newlines as 2 items, not one.
$ ls -1 | wc -l
10
One method to get around this, if using the GNU implementation of find
is to make use of find
's ability to print something else in place of each file that it finds and then count those instead.
Example
$ find . -printf . | wc -c
9
Here we're finding everything in the current directory (except ..
), and printing a dot (.
) for each, and then counting the dots using wc
's ability to count bytes instead of lines, wc -c
.
References
- bash - What is the best way to count find-results?
If you want to get a break down of the number of each type of file recursively under some dir, with GNU find
, you could do:
find /some/dir/. ! -name . -printf '%y\n' | sort | uniq -c | sed '
s/f/regular files/;t
s/d/directories/;t
s/l/symbolic links/;t
s/s/Unix domain sockets/;t
s/b/block devices/;t
s/c/character devices/;t
s/p/FIFOs/;t
s/D/Doors/;t
s/n/network special files/;t
s/.$/others (&)/'
On /usr/bin
on my system, that gives:
3727 regular files
710 symbolic links
On /dev
:
83 block devices
203 character devices
31 directories
426 symbolic links
1 FIFOs
1 Unix domain sockets
For symlinks, if you'd rather count them as the type of the file they point to rather than symbolic links
, you can change it to:
find /some/dir/. ! -name . -printf '%Y\n' | sort | uniq -c | sed '
s/f/regular files/;t
s/d/directories/;t
s/N/broken symbolic links/;t
s/s/Unix domain sockets/;t
s/b/block devices/;t
s/c/character devices/;t
s/p/FIFOs/;t
s/D/Doors/;t
s/n/network special files/;t
s/.$/others (&)/'
Which now gives for my /usr/bin
:
1 directories
4434 regular files
2 broken symbolic links
(a broken symlink is a symlink to a file for which find
cannot determine the type either because the file doesn't exist, or is in a directory you don't have access to or there's a loop in the resolution of the path of the file. In my case, those 2 where symlinks to files that are now gone).
None of those count .
and ..
. If you wanted them included (why would you?), there's no other way with find
than assume they're there for every directory and count them systematically:
find /some/dir/. -printf '%y\n' \( -name . -printf 'd\n' -o \
-type d -printf 'd\nd\n' \) | sort | uniq -c | sed '
s/f/regular files/;t
s/d/directories/;t
s/l/symbolic links/;t
s/s/Unix domain sockets/;t
s/b/block devices/;t
s/c/character devices/;t
s/p/FIFOs/;t
s/D/Doors/;t
s/n/network special files/;t
s/.$/others (&)/'
Which then gives on my /usr/bin
:
2 directories
3727 regular files
710 symbolic links
If you don't have access to the GNU find
, you can rewrite the first one as:
find /some/dir/. ! -name . \( \
-type f -exec printf '%.0sregular files\n' {} + -o \
-type d -exec printf '%.0sdirectories\n' {} + -o \
-type l -exec printf '%.0ssymbolic links\n' {} + -o \
-type s -exec printf '%.0sUnix domain sockets\n' {} + -o \
-type b -exec printf '%.0sblock devices\n' {} + -o \
-type c -exec printf '%.0scharacter devices\n' {} + -o \
-type p -exec printf '%.0sFIFOs\n' {} + -o \
-exec printf '%.0sothers\n' {} + \) | sort | uniq -c
Now, strictly speaking, we've not been counting files but directory entries. A directory like /usr/bin
typically has several entries that point to the same file. For instance, here, I have:
$ ls -lid /usr/bin/{nvi,nview,nex}
672252 -rwxr-xr-x 3 root root 434616 May 25 07:40 /usr/bin/nex
672252 -rwxr-xr-x 3 root root 434616 May 25 07:40 /usr/bin/nvi
672252 -rwxr-xr-x 3 root root 434616 May 25 07:40 /usr/bin/nview
Those are 3 directory entries (aka file names aka hard links) to the same file (the one with inode 672252. To count files instead of directory entries and with GNU find
and GNU uniq
(ignoring .
and ..
files which anyway are hard links to other directories):
find /some/dir/. ! -name . -printf '%y\t%D:%i\n' |
sort -u |
cut -f1 |
uniq -c |
sed '
s/f/regular files/;t
s/d/directories/;t
s/l/symbolic links/;t
s/s/Unix domain sockets/;t
s/b/block devices/;t
s/c/character devices/;t
s/p/FIFOs/;t
s/d/Doors/;t
s/n/network special files/;t
s/.$/others (&)/'
On my /usr/bin
, that gives:
3711 regular files
710 symbolic links