The result of "ls | wc -l" does not match the real number of files
Solution 1
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.
Solution 2
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.
Solution 3
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 ?
).
Solution 4
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 getwc -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
Related videos on Youtube
user1420706
Updated on September 18, 2022Comments
-
user1420706 over 1 year
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 thetotal 44
as a file, which is not correct.-
John1024 over 7 years
wc -l
is working as it should. Please run the commandtype ls
and report what you see. -
user1420706 over 7 yearsthe result of "type ls" is "ls is aliased to " ls -cml"
-
cutrightjm over 7 yearsIt returns 6 because
wc -l
counts the number of lines... it's including the line that saystotal 44
. -
Sundeep over 7 yearssee also: unix.stackexchange.com/questions/1125/…
-
ilkkachu over 7 years
-
-
user1420706 over 7 yearsIf I use /bin/ls | wc -l it will count 5 instead of 6
-
user1420706 over 7 yearsAlso, echo $((
ls|wc -l
-1)) also give the correct count. Would you like to explain what does your command really do? -
Julie Pelletier over 7 yearsIt subtracts 1 from the result of
wc
on your aliasedls
. -
Julie Pelletier over 7 yearsIf 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
. -
Sundeep over 7 yearswhy not use
-exec
instead of piping the result... -
Stéphane Chazelas over 7 yearsNote that
"ls"
won't help ifls
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. -
ilkkachu over 7 yearsHmm... on bash:
alias command="echo foo"
,command ls
, outputsfoo ls
. Wonderful. At least it doesn't accept/bin/ls
as an alias name. -
Random832 over 7 years@spasic because that would count the lines inside each file instead of number of files
-
Sundeep over 7 yearsoops, good point :).. searching for
find + wc
gave this good Q&A - stackoverflow.com/questions/1412244/… -
Stéphane Chazelas over 7 years@ikkachu, the idea is that it's common to have a function for
ls
(likels() { [ ! -t 1 ] || set -- -F "$@"; command ls "$@"; }
), while it's uncommon forcommand
to be an alias (except on AT&T ksh wherecommand
is a builtin alias) -
123 over 7 yearsWill break if filename contains newline.
-
roaima over 7 yearsIf you try
ls | cat
you'll see thatls
automatically switches tols -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.) -
badp over 7 yearsyou 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. -
Rabin over 7 years@123, in this uniq case you can use
find . -type f -print0 | wc --files0-from=-
which usenull
as the delimiter.