There are multiple ways on how to count the number of files in a directory on Linux. Some are less reliable than others and may even lead to an inacurate count of files. This post covers the pitfalls and best practices for three different approaches using ls
, find
, and the bash
shell using globbing and a bash array.
How to count files using the ls and wc commands?
Most solutions you will find online will try to parse a ls
command output which is piped through the wc
command line, sometimes adding a grep
in the mix. A common example is ls -la | wc -l
. This approach is not reliable and should be avoided.
Besides the two forked processes for ls
and wc
, the ls
output should never be parsed as a file name may contain a newline character which would lead to bad results when using ls
and wc
. In that example, the count will also be incorrect as it would include the .
(current directory) and ..
(parent directory) references.
A better version would be to use ls -1bA | wc -l
which would skip the .
and ..
from the ls
output, escape non-printable characters, and force the output to be one entry per line.
[me@linux ~]$ ls -a tmp/
. .. .one file1 file2
[me@linux ~]$ ls -a tmp/ | wc -l
5
[me@linux ~]$ ls -1bA tmp/*
tmp/.one
tmp/file1
tmp/file2
[me@linux ~]$ ls -1bA | wc -l
3
How to count files using the find and wc commands?
Another solution, similar to the previous one, is to use the find
command line with wc
, example: find tmp/ -type f | wc -l
.
This approach will suffer from a similar pitfall as the ls
one since file names may contain non-printable characters, including newlines. The output may not be reliably parsed. To overcome this, we can use the printf
option of the GNU find utility. Note, that this is not a standard POSIX option.
By using printf
we can skip the filename output and replace it with a dot (or any other single character) for each file found, then only count the total characters using wc -c
, example: find tmp -type f -printf '.' | wc -c
.
👉 To install GNU Find on macOS, you can use the homebrew package manager with the command
brew install findutils
. The find command will be installed asgfind
by default.
[me@linux ~]$ find tmp -type f
tmp/file2
tmp/file1
tmp/.one
[me@linux ~]$ find tmp -type f | wc -l
3
[me@linux ~]$ find tmp -type f -printf '.'
...
[me@linux ~]$ find tmp -type f -printf '.' | wc -c
3
How to count files using the Bash shell only?
You don’t need to use other command-line tools to count the number of files in a directory in bash. Instead of the previous solutions, without extra fork(), you can use
bash globbing with the nullglob
and dotglob
options with a
bash array and optionally a
bash if statement.
- The
nullglob
option will expand to a null string (empty value) when no files match the filename patterns. - The
dotglob
option is used to also list hidden files starting with a dot (.
). You can skip this option if you only want to count files that don’t start with a dot.
Make sure to set (using shopt -s nullglob dotglob
) and unset (using shopt -u nullglob dotglob
) the options before and after your test as it may impact the rest of your script behavior, especially with the nullglob
option.
[me@linux ~]$ shopt -s nullglob dotglob
[me@linux ~]$ matched_files=(tmp/*)
[me@linux ~]$ if ! (( ${#matched_files[*]} )); then
echo "Directory is empty"
else
echo "The directory contains ${#matched_files[@]} items which include files, symlink, directories, etc."
fi
# Example Output: The directory contains 3 items which include files, symlink, directories, etc.
[me@linux ~]$ shopt -u nullglob dotglob
You can optionally set the shopt in a subshell to ensure that the rest of your script is not impacted, at the cost of an additional fork(). Below is an example with a bash subshell to check if a directory is empty. Remember that you cannot access variables defined in a subshell, so the use cases may be limited or less readable.
[me@linux ~]$ if (shopt -s nullglob dotglob; files=(tmp/*); ((${#files[@]}))); then
echo "The directory is not empty";
fi
The directory is not empty