226

I have a directory with a large number of files. I don't see a ls switch to provide the count. Is there some command line magic to get a count of files?

17 Answers 17

288

Using a broad definition of "file"

ls | wc -l

(note that it doesn't count hidden files and assumes that file names don't contain newline characters).

To include hidden files (except . and ..) and avoid problems with newline characters, the canonical way is:

find . ! -name . -prune -print | grep -c /

Or recursively:

find .//. ! -name . -print | grep -c //
| improve this answer | |
  • 24
    wc is a "word count" program. The -l switch causes it to count lines. In this case, it's counting the lines in the output from ls. This is the always the way I was taught to get a file count for a given directory, too. – Sandy Aug 24 '10 at 6:07
  • 23
    please add note that ls does ls -1 if the output is a pipe. – lesmana Aug 24 '10 at 16:47
  • 6
    that doesn't get everything in a directory - you've missed dot files, and collect a couple extra lines, too. An empty directory will still return 1 line. And if you call ls -la, you will get three lines in the directory. You want ls -lA | wc -l to skip the . and .. entries. You'll still be off-by-one, however. – warren Aug 25 '10 at 15:14
  • 1
    An empty directory returns 0 for me – James Roth Sep 24 '13 at 2:16
  • 2
    A corrected approach, that would not double count files with newlines in the name, would be this: ls -q | wc -l - though note that hidden files will still not be counted by this approach, and that directories will be counted. – godlygeek Mar 3 '15 at 22:30
35

For narrow definition of file:

 find . -maxdepth 1 -type f | wc -l
| improve this answer | |
  • And you can of course omit the -maxdepth 1 for counting files recursively (or adjust it for desired max search depth). – user7089 Aug 3 '14 at 16:25
  • 1
    If you have a file whose name contains a newline, this approach will incorrectly count it twice. – godlygeek Mar 3 '15 at 22:20
  • 8
    A corrected approach, that would not double count files with newlines in the name, would be this: find -maxdepth 1 -type f -printf "\n" | wc -l – godlygeek Mar 3 '15 at 22:27
  • +1 Allow for hidden files and ignores directories – Michael Durrant Mar 12 '15 at 23:56
15
ls -1 | wc -l

...

$ ls --help | grep -- '  -1'
    -1                         list one file per line

...

$ wc --help | grep -- '  -l'
    -l, --lines            print the newline counts

PS: Note ls -<number-one> | wc -<letter-l>

| improve this answer | |
  • 10
    Most versions of ls do -1 automatically when the output is to a pipe. – Paused until further notice. Aug 24 '10 at 1:01
  • 3
    @Dennis that's interesting I didn't know that an application could tell its output was going to a pipe. – xenoterracide Aug 24 '10 at 5:35
  • 1
    I +'ed this version since it is more explicit. Though, yes ls does use -1 if it's piped (try it: ls | cat), I find the -1 syntax more explicit. – gabe. Aug 24 '10 at 18:13
  • 3
    @xenoterracide: In Bash: [[ -p /dev/stdin ]] && echo "stdin is from a pipe" – Paused until further notice. Aug 25 '10 at 22:42
  • 2
    In my tests it was significantly faster to also provide the -f option to avoid ls sorting the filenames. Unfortunately you still get the wrong answer if your filenames contain newlines. – Samuel Edwin Ward Jan 8 '13 at 20:42
14

Probably the most complete answer using ls/wc pair is

ls -Aq | wc -l

if you want to count dot files, and

ls -q | wc -l

otherwise.

  • -A is to count dot files, but omit . and ...
  • -q make ls replace nongraphic characters, specifically newline character, with ?, making output 1 line for each file

To get one-line output from ls in terminal (i.e. without piping it into wc), -1 option has to be added.

(behaviour of ls tested with coreutils 8.23)

| improve this answer | |
  • 2
    As you said, -1 is not needed. As to "it handles newlines in filenames sensibly with console output", this is because of the -q switch (that you should use instead of -b because it's portable) which "Forces each instance of non-printable filename characters and <tab> characters to be written as the <question-mark> ( '?' ) character. Implementations may provide this option by default if the output is to a terminal device." So e.g. ls -Aq | wc -l to count all files/dirs or ls -qp | grep -c / to count only non-hidden dirs etc... – don_crissti May 13 '15 at 11:08
  • Thanks for your input. Changed -b to -q. – Frax May 14 '15 at 15:05
  • Currently includes directories in its file count. To be most complete we need an easy way to omit those when needed. – Josh Habdas May 12 at 7:48
  • @JoshHabdas It says "probably". ;) I think the way to omit directories would be to use don_crissti's suggestion with a slight twist: ls -qp | grep -vc /. Actually, you can use ls -q | grep -vc / to count all (non-hidden) files, and adding -p makes it match only regular files. – Frax May 12 at 11:55
12

I have found du --inodes useful, but I'm not sure which version of du it requires. It should be substantially faster than alternative approaches using find and wc.

On Ubuntu 17.10, the following works:

du --inodes      # all files and subdirectories
du --inodes -s   # summary
du --inodes -d 2 # depth 2 at most

Combine with | sort -nr to sort descending by number of containing inodes.

| improve this answer | |
8

If you know the current directory contains at least one non-hidden file:

set -- *; echo "$#"

This is obviously generalizable to any glob.

In a script, this has the sometimes unfortunate side effect of overwriting the positional parameters. You can work around that by using a subshell or with a function (Bourne/POSIX version) like:

count_words () {
  eval 'shift; '"$1"'=$#'
}
count_words number_of_files *
echo "There are $number_of_files non-dot files in the current directory"

An alternative solution is $(ls -d -- * | wc -l). If the glob is *, the command can be shortened to $(ls | wc -l). Parsing the output of ls always makes me uneasy, but here it should work as long as your file names don't contain newlines, or your ls escapes them. And $(ls -d -- * 2>/dev/null | wc -l) has the advantage of handling the case of a non-matching glob gracefully (i.e., it returns 0 in that case, whereas the set * method requires fiddly testing if the glob might be empty).

If file names may contain newline characters, an alternative is to use $(ls -d ./* | grep -c /).

Any of those solutions that rely on passing the expansion of a glob to ls may fail with a argument list too long error if there are a lot of matching files.

| improve this answer | |
  • 1
    Do you really want to create 13,923 positional parameters? And you should make your local variable local or eliminate it: eval $1=$# or just use echo $# and do number_of_files=$(count_words *). – Paused until further notice. Aug 24 '10 at 1:16
  • 1
    @Dennis: part of the point was to avoid forking. I guess that's not a 21st century concern. Ok, I admit I don't care about non-POSIX shells any more, so I could have avoided the temporary variable. – Gilles 'SO- stop being evil' Aug 24 '10 at 7:14
  • Why did you subtract one from $# (you hadn't done that prior to the edit)? – Paused until further notice. Aug 24 '10 at 22:12
  • @Dennis: I'm still avoiding a fork (well, it does make a difference on machines with a slow CPU such as routers) and passing a variable name as $1. So what I want to count is the number of parameters that aren't the first parameter. (I can't use shift because I need to keep the variable name around.) (Umm, now if you'd asked about the first line...) – Gilles 'SO- stop being evil' Aug 24 '10 at 22:42
  • @Dennis: come to think of it, I can use shift if I time it right. – Gilles 'SO- stop being evil' Aug 24 '10 at 22:50
5

While using ls/wc pair if we are adding -U it will be much faster (do not sort ).

ls -AqU | wc -l
| improve this answer | |
4

After installing the tree command, just type:

tree

If you want hidden files too:

tree -a

If you are using Debian / Mint / Ubuntu Linux, type the following command to install the tree command:

sudo apt-get install tree

The option -L is used for specifying the maximum display level of the directory tree. The tree command does not only count the number of files, but also the number of directories, considering as many levels of the directory tree as you like.

| improve this answer | |
  • When I type tree, I get a sort of tree output to the screen of the directory I am in but I cannot see where the number of files is shown. – charlesdarwin Apr 25 '18 at 12:30
4
find -maxdepth 1 -type f -printf . | wc -c
  • -maxdepth 1 will make it non-recursive, find is recursive by default
  • -type f will include files only
  • -printf . is a cute touch. it prints a dot for each file instead of the filename, and now this is able to handle any filename and also saves data; we just have to count the dots :)
  • | wc -c counts characters
| improve this answer | |
1

No pipe, no string copy, no fork, just plain bash one liner

$ fcount() { local f i=0; for f in *; do let i++; done; echo $i; }; fcount
| improve this answer | |
  • 1
    Gives a value of 1 if no files in directory. Doesn't count files, counts files and directories. – steve Feb 2 '17 at 21:42
1

Here's another technique along the lines of the one Gilles posted:

word_count () { local c=("$@"); echo "${#c[@]}"; }
file_count=$(word_count *)

which creates an array with 13,923 elements (if that's how many files there are).

| improve this answer | |
  • What's the point of that c array? word_count() { echo "$#"; } would be enough. The point of @Gilles solution is to store the count in a returned variable to avoid having to use command substitution (which involves a fork and pipe in shells other than ksh93). – Stéphane Chazelas Mar 1 '16 at 16:27
1
find . -type f -maxdepth 1 |  wc -l 

This can list only the files in current directory.

| improve this answer | |
  • find . -type f will find files in the current directory, and also, recursively, in sub-directories. – dhag Jun 16 '16 at 20:23
1

On Linux, to make the command very robust and handle files that might have newlines in their name, use this:

find -maxdepth 1 -type f -print0 | tr -cd '\0' | wc -c

This saves us from the ordeal of parsing ls output.


Related:

| improve this answer | |
0

Try this i hope this answer will help you

echo $((`ls -l | wc -l` -1 ))
| improve this answer | |
0

You can check with:

ls -l | grep -v ^l | wc -l
| improve this answer | |
0

Improving some answers given before but this time doing explicitly.

$ tree -L 1 | tail -n 1 | cut -d " " -f 3

It's worthy to notice the use of some loved commands like tail and cut. Also, note that tree is not available by default. The command above first capture information about the directory at level 1, then get the last line tail -n 1 where our goal is, and end up with cut to take the third word.

For instance, locating in /:

/ $ tree -L 1
.
├── 1
├── bin -> usr/bin
├── boot
├── dev
├── etc
├── home
├── lib -> usr/lib
├── lib64 -> usr/lib64
├── lost+found
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin -> usr/sbin
├── srv
├── sys
├── tmp
├── usr
└── var

20 directories, 1 file
/ $ tree -L 1 | tail -n 1
20 directories, 1 file
/ $ tree -L 1 | tail -n 1 | cut -d " " -f 3
1

Then, what about asking the number of directories?

| improve this answer | |
-2

Use the tree command, just:

tree
| improve this answer | |

Not the answer you're looking for? Browse other questions tagged or ask your own question.