3

I need to count the number of files under a folder and use the following command.

cd testfolder
bash-4.1$ ls | wc -l
6

In fact, there are only five files under this folder,

bash-4.1$ ls
total 44
-rw-r--r-- 1 comp 11595 Sep  4 22:51 30.xls.txt
-rw-r--r-- 1 comp 14492 Sep  4 22:51 A.pdf.txt
-rw-r--r-- 1 comp  8160 Sep  4 22:51 comparison.docx.txt
-rw-r--r-- 1 comp   903 Sep  4 22:51 Survey.pdf.txt
-rw-r--r-- 1 comp  1206 Sep  4 22:51 Steam Table.xls.txt

It looks like ls | wc -l even counts the total 44 as a file, which is not correct.

5 Answers5

12

Just a extra info for the above,

You should use find instead of ls if you like to process the output, it has some futures which are more suitable (e.g. -print0) for piping the result to other applications.

In the above case you can use it like this,

find . -type f | wc -l

which will list any files on the current directory.

Rabin
  • 3,883
  • 1
  • 22
  • 23
10

It's not working because wc -l returns the number of lines of the output of the ls command, which in this case includes total 44. Since your shell has an alias for ls as ls -cml, you're getting that extra information which is messing up your output.

Instead, use the command "ls" -Aq | wc -l. The -A command lists all files in the directory including dotfiles, but excludes . and ... The quotations here are important - they ignore the alias and run /bin/ls directly.

-q makes sure that file names are all printed on one line only even if they contain newline characters (which would then be rendered as ?).

cutrightjm
  • 5,290
  • 1
    Note that "ls" won't help if ls is defined as a function instead of an alias (or if there's also an alias for "ls" though that's less likely and not even supported by some shell). command ls may be more foolproof. – Stéphane Chazelas Sep 05 '16 at 07:10
  • Hmm... on bash: alias command="echo foo", command ls, outputs foo ls. Wonderful. At least it doesn't accept /bin/ls as an alias name. – ilkkachu Sep 05 '16 at 07:48
  • I find "ls" a bit unclear. /bin/ls explicitly conveys what you are doing. – Martin Nyolt Sep 05 '16 at 08:55
  • @ikkachu, the idea is that it's common to have a function for ls (like ls() { [ ! -t 1 ] || set -- -F "$@"; command ls "$@"; }), while it's uncommon for command to be an alias (except on AT&T ksh where command is a builtin alias) – Stéphane Chazelas Sep 05 '16 at 10:01
  • you can also say env ls. env doesn't know about your shell aliases, functions etc., and if you call it like that it's a no-op. – badp Sep 05 '16 at 21:50
8

wc is a char, word, and line counter, not a file counter.

You, the programmer/script writer, are responsible for making it count what you want and to adjust the calculation accordingly.

In your case, you could do something like:

echo $((`ls|wc -l`-1))

Finally note that your ls is probably an alias as it gives a long listing which is not the normal ls without arguments. It may therefore be a good idea to refer to ls's full path (usually /bin/ls) to avoid confusion.

1

As others have already mentioned, doing ls | wc -l is not always a reliable way to get files count in a directory.

Here are some reliable ways:

  • You can get find to print a . for each file found and get wc -l to count the number of lines:

     find . -type f -printf '.\n' | wc -l
    
  • If there are not many files in the directory, you can save the file names in an array and then get the length of the array:

     for f in *; do [ -f "$f" ] && files+=("$f"); done && echo "${#files[@]}"
    

    For all files and directories, this gets easier:

     files=( * ) && echo "${#files[@]}"
    

Example:

$ touch $'foo\nbar' 'foo bar' spam                         

$ ls | wc -l                      
4

$ find . -type f | wc -l                                   
4

$ find . -type f -printf '.\n' | wc -l
3

$ for f in *; do [ -f "$f" ] && files+=("$f"); done

$ echo "${#files[@]}"
3
heemayl
  • 56,300
0
bash-4.1$ ls
1. total 44
2. -rw-r--r-- 1 comp 11595 Sep  4 22:51 30.xls.txt
3. -rw-r--r-- 1 comp 14492 Sep  4 22:51 A.pdf.txt
4. -rw-r--r-- 1 comp  8160 Sep  4 22:51 comparison.docx.txt
5. -rw-r--r-- 1 comp   903 Sep  4 22:51 Survey.pdf.txt
6. -rw-r--r-- 1 comp  1206 Sep  4 22:51 Steam Table.xls.txt

That's correct, there are 6 lines in the output. You might want to use: ls -1 which corresponds to single column format and it looks like this:

ls -1
Desktop
Documents
Downloads
Music
Pictures
Public
Templates
Videos

Now wc -l returns correct number of files:

ls -1 | wc -l
8
agilob
  • 103
  • 4
  • 2
    If you try ls | cat you'll see that ls automatically switches to ls -1 format when talking to something that isn't a terminal. Unless it's been overridden with an alias that stops it doing so. (Which is what was happening to the OP.) – Chris Davies Sep 05 '16 at 15:09