1

I want to know if there is a way to use a loop to check the files for the numbers of files in my current directory without pipes.

If I have 3 files, the output should be 3.

I know you can easily do

ls | wc -l 

but I want to know if there is a way to put this in a loop and not use pipes.

Thanks

1
  • Bonus for solutions that work properly if there are filenames which contain spaces or \n. Mar 7, 2016 at 5:04

3 Answers 3

3

In bash, you can add all files to an array and echo the number of elements in the array:

arr=(*)
echo "${#arr[@]}"
2

Here you go :

files_num=0
for i in *; do 
  (( files_num++ ))
done
echo $files_num
1
  • I used loop, because OP was asking for it, but indeed I agree your solution is more elegant :)
    – MAQ
    Mar 6, 2016 at 21:18
2

Sure, you can use a loop, but why would you use a loop when you don't need one?

The shell has all the tools to enumerate the files in a directory and to count them. The basic idea (packaged in a function) is

count_files () {
  set *
  echo $#
}

i.e. set the positional arguments to the list of names of files in the current directory and print the number of positional arguments. This breaks down in several edge cases:

  • If the first file name starts with -, it's interpreted as an option to set. Easily solved with set -- *.
  • If there are no matching files, the * is left unchanged. If you want to be portable to POSIX shells, you need to handle this special case. If you can afford to require ksh, bash or zsh, they have better ways.
  • This excludes dot files (files beginning with a .). They need to be included separately. The glob .* lists dot files, including . and .. except in pdksh and all other descendants of the Forsyth shell and zsh (and bash if GLOBIGNORE is not empty). Assuming that those should not be counted, just subtract 2.

POSIX solution:

count_files () {
  set -- * .*
  if [ "$1" = "*" ] && ! [ -e "$1" ] && ! [ -L "$1" ]; then
    shift # Remove the * which didn't match any file
  fi
  echo $#
}

Ksh93 solution:

function count_files {
  typeset FIGNORE='@(.|..)'
  set -- ~(N)
  echo $#
}

Bash solution:

count_files () (
  shopt -s dotglob nullglob
  set -- *
  echo $#
)

Zsh solution:

count_files () {
  emulate -LR zsh
  set -- *(DN)
  echo $#
}
5
  • Why would you mention set * at all? Why would you invoke the split+glob operator on $#? Mar 6, 2016 at 21:23
  • @StéphaneChazelas How would you count the matches without set *? If I'd used an array variable, which is only an option in shells that have (named) arrays, would you have asked why I mention arrays at all? As for $#, robustness against crazy IFS settings elsewhere in the script is something I don't care much about (unlike robustness wrt file names, because they aren't under control of the script). Mar 6, 2016 at 21:36
  • I meant set * as opposed to set -- *. IMV, that's nothing to do with robustness. By writing $# without quotes, you're asking the shell to split it which doesn't make any sense (and yes, that means that code depends on the current value of $IFS). Mar 6, 2016 at 21:41
  • @StéphaneChazelas I first present comprehensible code, and then robust code. It's a matter of didacticism. Writing set -- * is less clear, at least to people who aren't shell programming experts (which is the audience of this answer: if they knew all that stuff, they wouldn't need to ask). I first present code that's decently readable but doesn't work in all cases, and then robust but hard-to-read code. Same thing with $# vs "$#": a common question about "$#" is “but don't the quotes prevent it being parsed as an integer?” by analogy with other languages where it does. Mar 6, 2016 at 21:46
  • But the first thing you show is wrong code. Most people come here for a quick answer to their question and won't read past the first piece of code that seems to work. Also, you're introducing the concept that it might be OK to leave variables unquoted. echo * or $# is not more comprehensible, it's just wrong. It's the shell syntax that's awkward, not much you can do about it. Mar 6, 2016 at 21:54

You must log in to answer this question.

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