1

I learned that 'ls | wc -l' command prints number of files in the current directory, but it includes all files and directories. Can I do the same task only for pure files excluding directories?

user67275
  • 233

3 Answers3

3

directory is just one of many types of files. Other types include symbolic link, fifo, device, regular, socket...

In the output of ls -n, the first character indicates the type. d for directory, - for regular, l for symlink, so you could do:

LC_ALL=C ls -Aqn | LC_ALL=C grep  -c '^-' # regular files only
LC_ALL=C ls -Aqn | LC_ALL=C grep -vc '^d' # anything but directory

(remove the -A option if you don't want to count hidden files).

You could also do the check after symlink resolution,

LC_ALL=C ls -LAqn | LC_ALL=C grep  -c '^-' # regular files or symlinks to regulars
LC_ALL=C ls -LAqn | LC_ALL=C grep -vc '^d' # anything but directory and symlinks to dirs.

With the zsh shell, you can also do it using globbing:

(){print $#} *(NDoN.)  # regular
(){print $#} *(NDoN^/) # non-directories

After symlink resolution (adding the - glob qualifier):

(){print $#} *(NDoN-.)  # regular
(){print $#} *(NDoN-^/) # non-directories

(remove the D qualifier if you don't want to count hidden files).

oN is to disable the sorting of the file list as an optimisation as we don't care about the order. The GNU implementation of ls has a -U option for that.

In any case, your ls | wc -l is wrong as it counts the number of newline characters used by ls to delimit the file names (so one per file), but also the newline characters in the names of the file (it also omits hidden files). Using the -q option works around it as it causes newline characters in filenames to be rendered as ?.

1

I would normally use:

find . -maxdepth 1 -type f | wc -l

Beware that:

  • this also count file names starting with .
  • only looks at regular files (not fifo, symlinks etc)
Ole Tange
  • 35,514
  • 1
    That counts the number of lines in the file names, not the number of files. Use find . -maxdepth 1 -type f | LC_ALL=C grep -c / to count the number of regular files. Also not that -maxdepth is a non-standard GNU extension. Use find . ! -name . -prune -type f | LC_ALL=C grep -c / posixly (equivalent of -mindepth 1 -maxdepth 1) – Stéphane Chazelas Apr 05 '21 at 08:19
  • that should be | LC_ALL=C grep -c '^\./' - otherwise you'll still count partial path-names with vertical spaces like linefeeds in directory names earlier in the path, like ./foo<LF>bar/bar<LF>foo/filename. Actually, even that regex can still be fooled by things like ./foo<LF>./bar/filename – cas Apr 05 '21 at 12:50
  • so use find with -print0 and grep -z -c ... (GNU grep, maybe others too, i can't remember right now). GNU wc also supports --files0-from=- for NUL-separated stdin. – cas Apr 05 '21 at 12:56
0

I am not yet able to post a comment but here is a bash-specific solution. It is not as succinct but might feel cleaner without the use of grep.

Since you used just ls I assume that you do not need hidden files.

i=0
for f in *
do
[[ -f "$f" ]] && let "i++"
done
echo $i

Or combined into a one-liner, i=0; for f in *; do [[ -f "$f" ]] && let "i++"; done; echo $i

  • Note that it counts regular files and symlinks to regular files, excluding hidden ones, so would be the equivalent of zsh's (){print $#} *(NoN-.) or ls -Lqn | LC_ALL=C grep -c '^-' – Stéphane Chazelas Apr 05 '21 at 08:11