0

I am trying to print the number of directories in the working directory. For some reason my counter is not incrementing. Can you please tell me why?

#!/bin/bash
n=0
for afile in $(ls)
do
    if [ -d $afile ]
    then
      (( n ++ ))
    fi
done
echo There are $n directories under the current directory $(pwd).
exit 0

Oddly, this program seems to iterate through ls .. (up directory)

for myfile in $(ls ..)
do
    if [ -d "../$myfile" ]
    then
      echo "../$myfile (dir)"
    else
      echo ../$myfile
    fi
done
exit 0
  • Sorry, I think we edited at the same time. You might want to add back your changes. – Sparhawk Mar 2 '18 at 0:37
  • Are you sure? I just refreshed and the code block looks OK now. – Benjamin Mar 2 '18 at 0:38
  • Oh okay, maybe we made the same change anyway! – Sparhawk Mar 2 '18 at 0:38
  • So when you run this, is the output always 0 directories? It works for me, although you need to quote properly. – Sparhawk Mar 2 '18 at 0:39
  • Yes, it's always 0. Can you explain what is wrong with my quotes? – Benjamin Mar 2 '18 at 0:41
1

For counting the number of non-hidden directories (in the current directory), using bash:

shopt -s nullglob
set -- */
printf 'There are %d non-hidden subdirectories in %s\n' "$#" "$PWD"

To include the count of hidden directories:

shopt -s dotglob nullglob
set -- */
printf 'There are %d subdirectories in %s\n' "$#" "$PWD"

What these pieces of code do is to expand the pattern */ and to count the number of names that the pattern expands to. The pattern, since it ends with a slash, will only expand to directory names (or to names of symbolic links to directories).

The directory names will be assigned to the positional parameters, $1, $2 etc. using set, and the number of these parameters is kept in $# by the shell (so there's no need to actually loop over them to count them).

If you feel more comfortable with bash arrays:

shopt -s dotglob nullglob
dirs=( */ )
printf 'There are %d subdirectories in %s\n' "${#dirs[@]}" "$PWD"

This is essentially the same thing, except it uses a named array instead of the positional parameters.

The dotglob shell option, in bash, will make * match hidden names as well as non-hidden names. The nullglob shell option will make non-matching patterns expand to nothing.

Related:

  • Your solution will print 1 in the case where there are no subdirectories. You may use the unportable nullglob; shopt -s nullglob; set -- */ .[!.]/ .??*/. – Uncle Billy Jan 11 '19 at 12:07
  • @UncleBilly Thanks. It should be enough with nullglob for bash though, and a test on $1 in /bin/sh (which I'm not covering here). I will update ASAP. – Kusalananda Jan 11 '19 at 12:14
0

Your code works fine for me, but there are a few errors in it that will fail in some cases (e.g. if a filename has a space in it).

  • Firstly, don't parse ls. Instead iterate using a glob. i.e. instead of $(ls), use *.
  • When reading variables, you should quote them, i.e. use "$foo" instead of $foo. If you do not quote them, the shell will split the output on the IFS (i.e. the spaces). You can test this with your script. Include some directories with no spaces, then test with a space. Only the former will be counted.

Here is the fixed code:

#!/bin/bash
n=0
for afile in *
do
    if [ -d "$afile" ]
    then
      (( n ++ ))
    fi
done
echo There are $n directories under the current directory $(pwd).
exit 0

I'm not sure if this is obvious, but the script will run in the current directory.

  • Thanks Sparhawk! That definitely works. But in my assignment I am required to use the ls command. Is there ANY reasonable way to parse and iterate through ls? – Benjamin Mar 2 '18 at 1:04
  • 1
    @Benjamin not really. This is an example: unix.stackexchange.com/q/128985/117549 – Jeff Schaller Mar 2 '18 at 1:09
  • I agree that there's no way to parse ls that works for all files. You might be able to use something like find, but that probably not allowed either. One hacky way to count directories with ls is to use ls -F (see man ls) and search for /. – Sparhawk Mar 2 '18 at 1:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.