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
  • 5
    wc -l is working as it should. Please run the command type ls and report what you see.
    – John1024
    Sep 5, 2016 at 4:11
  • 1
    the result of "type ls" is "ls is aliased to " ls -cml"
    – user288609
    Sep 5, 2016 at 4:16
  • 15
    It returns 6 because wc -l counts the number of lines... it's including the line that says total 44.
    – cutrightjm
    Sep 5, 2016 at 4:23
  • 2
    see also: unix.stackexchange.com/questions/1125/…
    – Sundeep
    Sep 5, 2016 at 4:43
  • 5
    also this and this
    – ilkkachu
    Sep 5, 2016 at 8:03

5 Answers 5

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.

5
  • 1
    why not use -exec instead of piping the result...
    – Sundeep
    Sep 5, 2016 at 6:47
  • 3
    @spasic because that would count the lines inside each file instead of number of files
    – Random832
    Sep 5, 2016 at 9:32
  • oops, good point :).. searching for find + wc gave this good Q&A - stackoverflow.com/questions/1412244/…
    – Sundeep
    Sep 5, 2016 at 9:38
  • 1
    Will break if filename contains newline.
    – 123
    Sep 5, 2016 at 14:22
  • 1
    @123, in this uniq case you can use find . -type f -print0 | wc --files0-from=- which use null as the delimiter.
    – Rabin
    Sep 5, 2016 at 22:04
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 ?).

5
  • 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. Sep 5, 2016 at 7: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 5, 2016 at 7:48
  • I find "ls" a bit unclear. /bin/ls explicitly conveys what you are doing. Sep 5, 2016 at 8: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) Sep 5, 2016 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 5, 2016 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.

5
  • 1
    If I use /bin/ls | wc -l it will count 5 instead of 6
    – user288609
    Sep 5, 2016 at 4:17
  • Also, echo $((ls|wc -l-1)) also give the correct count. Would you like to explain what does your command really do?
    – user288609
    Sep 5, 2016 at 4:19
  • Of course. I showed you 2 solutions to show you how you can deal with calculations in bash. Sep 5, 2016 at 4:19
  • It subtracts 1 from the result of wc on your aliased ls. Sep 5, 2016 at 4:21
  • 2
    If you put it in a script, it probably won't have the alias and as I mentioned it would be a better idea to use /bin/ls|wc -l. Sep 5, 2016 at 4:22
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
2
  • @roaima you know what you just made me feel like a stupid now, i don't know what i was thinking then.
    – heemayl
    Sep 5, 2016 at 20:21
  • @roaima I have made an edit. All credit goes to you. Thanks.
    – heemayl
    Sep 5, 2016 at 20:27
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
1
  • 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.) Sep 5, 2016 at 15:09

Not the answer you're looking for? Browse other questions tagged .