Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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?

share|improve this question

5 Answers 5

up vote 19 down vote accepted

Using a broad definition of "file"

ls | wc -l
share|improve this answer
5  
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
    
please add note that ls does ls -1 if the output is a pipe. –  lesmana Aug 24 '10 at 16:47
4  
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
    
An empty directory returns 0 for me –  James Roth Sep 24 '13 at 2:16

For narrow (I hope) definition of file:

 find -maxdepth 1 -type f | wc -l
share|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 at 16:25
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>

share|improve this answer
5  
Most versions of ls do -1 automatically when the output is to a pipe. –  Dennis Williamson 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
    
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
1  
@xenoterracide: In Bash: [[ -p /dev/stdin ]] && echo "stdin is from a pipe" –  Dennis Williamson Aug 25 '10 at 22:42
    
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
set -- *; echo $#

This is obviously generalizable to any glob. The -- can be omitted if you're sure the first file doesn't begin with -.

In a script, this has the sometimes unfortunate side effect of overwriting the positional parameters. You can work around this with a function (Bourne/POSIX version, you can write it more clearly in fancy shells) (warning, typed directly into the browser):

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 * | 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 * 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).

share|improve this answer
    
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 *). –  Dennis Williamson Aug 24 '10 at 1:16
    
@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 Aug 24 '10 at 7:14
    
Why did you subtract one from $# (you hadn't done that prior to the edit)? –  Dennis Williamson 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 Aug 24 '10 at 22:42
    
@Dennis: come to think of it, I can use shift if I time it right. –  Gilles Aug 24 '10 at 22:50

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).

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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