1

I'm trying to write a bash script that counts all the files in the given directory & sub-directories, so I wrote this:

#!/bin/bash

var=0
if ["$#" == "0"]
    directory="$(echo pwd)"
  then
    directory=$1
fi

echo $directory;
for x in `ls -l $directory | grep "^-" | tr -s ' ' | cut -d ' ' -f 9`;
do
var=$((var+1))
done

for x in `ls -l $directory | grep "^d" | tr -s ' ' | cut -d ' ' -f 9`;
do
output = "$($0 $x)"
done

var=$((var+output))
echo $var

But I get something like this:

./lala2
./lala2: line 4: [0: command not found

test
./lala2: line 4: [1: command not found
./lala2: line 4: [1: command not found

Why is that? Are the variables global?

3

Replace:

if ["$#" == "0"]

with:

if [ "$#" = "0" ]

Some comments:

  1. In shell, spaces are important. If you want the shell to recognize the test command, [, there must be spaces around the [.

    Spaces are not important for shell keywords but [, unlike [[, is a command, not a keyword.

  2. The meaning of the error message [0: command not found is that "$#" evaluated to 0 so that the string ["$#" became [0 and bash could find no command by the name of [0.

  3. It is possible to use == for string equality in bash. It is, however, no portable. In POSIX, the correct equality operator in [...] is =. This becomes very important if you run your script, either intentionally or unintentionally, under a POSIX shell like dash.

Other issues

  1. Once again, spaces matter. When the shell sees this command:

    output = "$($0 $x)"
    

    it will try to execute a command called output and provide it with two arguments, the first being = and the second being the result of "$($0 $x)". Consequently, as Gordon Davisson points out, that line should be replaced with:

    output="$($0 $x)"
    

    Because there are no spaces around the =, the shell will treat this as a variable assignment.

  2. The script attempts to parse the output of ls and this is unreliable. In this case, it will lead to errors if any of the subdirectory names contain whitespace.

  3. As rici points out, the command directory="$(echo pwd)" likely does not do what you expect. Observe:

    $ directory="$(echo pwd)"
    $ echo "$directory"
    pwd
    

    This command assigns the string pwd to the variable directory. Probably, you want the current working directory, like this:

    directory=$(pwd)
    

    But, it is simpler than that. In Unix, the current working directory is always .:

    directory=.
    
  4. There are other reasons that this if-then statement does not do what one might hope:

    if ["$#" == "0"]
        directory="$(echo pwd)"
      then
        directory=$1
    fi
    

    You likely want to move the then statement and add an else statement:

    if [ "$#" = "0" ]
    then
        directory=.
    else
        directory="$1"
    fi
    

Alternative script

If the goal is to count the number of regular files in a directory and all its subdirectories, then try:

echo "$(find "$directory" -type f -printf "1\n" | wc -l)"

This approach is safe to use even if the file or directory names contain whitespace or other difficult characters.

  • Also, while spaces are required in a test (or [) command, they're forbidden in assignments. So output = "$($0 $x)" must be changed to output="$($0 $x)". (And I'd also say that spaces are significant for keywords as well as commands: [["$#" won't work any better than ["$#".) – Gordon Davisson Nov 10 '15 at 3:00
  • @GordonDavisson Yes. Excellent point. I added discussion of assignments to the answer. – John1024 Nov 10 '15 at 3:19
  • 1
    directory="$(echo pwd)" is also almost certainly wrong. (Unless directory=pwd was intended, in which case it is merely redundant.) – rici Nov 10 '15 at 3:45
  • @rici Good observation. I added a discussion of echo pwd to the answer also. – John1024 Nov 10 '15 at 4:02
  • 2
    Curiously, although your point 4 is 100% correct, the incorrect code will have the intended effect. directory=$(echo pwd) always succeeds, so the then clause will always be executed; if $1 is not assigned, then that will assign directory= and later on ls -l $directory will expand to ls -l instead of ls -l '', which is what you would get if the variable expansion had been correctly quoted. I think this is what is called a "comedy of errors". – rici Nov 10 '15 at 4:09

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.