Is there a bash command which counts files?
Is there a bash command which counts the number of files that match a pattern?
For example, I want to get the count of all files in a directory which match this pattern: log*
Solution 1:
This simple one-liner should work in any shell, not just bash:
ls -1q log* | wc -l
ls -1q will give you one line per file, even if they contain whitespace or special characters such as newlines.
The output is piped to wc -l, which counts the number of lines.
Solution 2:
You can do this safely (i.e. won't be bugged by files with spaces or \n
in their name) with bash:
$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
You need to enable nullglob
so that you don't get the literal *.log
in the $logfiles
array if no files match. (See How to "undo" a 'set -x'? for examples of how to safely reset it.)
Solution 3:
Lots of answers here, but some don't take into account
- file names with spaces, newlines, or control characters in them
- file names that start with hyphens (imagine a file called
-l
) - hidden files, that start with a dot (if the glob was
*.log
instead oflog*
- directories that match the glob (e.g. a directory called
logs
that matcheslog*
) - empty directories (i.e. the result is 0)
- extremely large directories (listing them all could exhaust memory)
Here's a solution that handles all of them:
ls 2>/dev/null -Ubad1 -- log* | wc -l
Explanation:
-
-U
causesls
to not sort the entries, meaning it doesn't need to load the entire directory listing in memory -
-b
prints C-style escapes for nongraphic characters, crucially causing newlines to be printed as\n
. -
-a
prints out all files, even hidden files (not strictly needed when the globlog*
implies no hidden files) -
-d
prints out directories without attempting to list the contents of the directory, which is whatls
normally would do -
-1
makes sure that it's on one column (ls does this automatically when writing to a pipe, so it's not strictly necessary) -
2>/dev/null
redirects stderr so that if there are 0 log files, ignore the error message. (Note thatshopt -s nullglob
would causels
to list the entire working directory instead.) -
wc -l
consumes the directory listing as it's being generated, so the output ofls
is never in memory at any point in time. -
--
File names are separated from the command using--
so as not to be understood as arguments tols
(in caselog*
is removed)
The shell will expand log*
to the full list of files, which may exhaust memory if it's a lot of files, so then running it through grep is be better:
ls -Uba1 | grep ^log | wc -l
This last one handles extremely large directories of files without using a lot of memory (albeit it does use a subshell). The -d
is no longer necessary, because it's only listing the contents of the current directory.