Jeyaram
Jeyaram

Reputation: 9474

Wrong output if no such file

I am trying to find out no.of files of particular type. If file exists then this script works fine, else for loop executed once(which is not expected to run).

echo "Checking for text files..."

j=0
for i in *.txt;
do
echo $i;
j=`expr $j + 1`;
done

if [ $j -ge 0 ];then
echo "No.of Text files:"$j
else
echo "No Text files."
fi

There may be some other way to same thing like ls *.txt |xargs .... But as a newbie I would like to know the problem in the posted script. I request you to share your knowledge in this regard.

Upvotes: 2

Views: 107

Answers (2)

Ansgar Wiechers
Ansgar Wiechers

Reputation: 200273

When there's no matching file for the pattern *.txt, it's interpreted as a literal string "*.txt". That's why your loop runs at least once.

There are several ways to deal with this issue. You could add a check for the presence of matching files like this:

if ls *.txt >/dev/null 2>&1; then
  for i in *.txt; do
    ...
  done
fi

Depending on what you want to do inside the loop, you could also use find:

find . -maxdepth 1 -name "*.txt" | while read i; do
  ...
done

Some things, however, won't work with this construct (like incrementing $j), because the while loop runs in a subshell, so all changes to $j are lost when the subshell exits (i.e. the loop terminates).

As a side note: I'd change the incrementation of $j from j=`expr $j + 1` to ((j++)).

Upvotes: 1

gniourf_gniourf
gniourf_gniourf

Reputation: 46823

There are no problems in your script, really, except that it could be written in a better style, using modern builtins and except that it fails if there are no text files, see below.

echo "Checking for text files..."

j=0
for i in *.txt; do
    echo "$i"
    ((++j))
done

if ((j!=0)); then
    echo "No. of Text files: $j"
else
    echo "No Text files."
fi

When using globbing, it's always a good practice to use either nullglob of failglob.

  • failglob will raise an error if the glob can't be expanded,
  • nullglob will just expand to nothing if there are no suitable expansions.

without these, *.txt will expand to *.txt (verbatim) if there are no suitable expansions, which is exactly what you want to avoid!

In your case, just put

shopt -s nullglob

at the top of your script and you're done!

Now a possible way to count the number of *.txt files could be:

shopt -s nullglob
a=(*.txt)
echo "There are ${#a[@]} text files"

What is this fuss about nullglob and failglob?

Let's go in a scratch directory:

$ mkdir scratch; cd scratch
$ # this dir is empty
$ echo *.txt
*.txt
$ 

because the glob has no suitable expansion there. Now:

$ shopt -s nullglob
$ echo *.txt

$ 

because of the nullglob. Now we'll unset nullglob and set failglob:

$ shopt -u nullglob
$ shopt -s failglob
$ echo *.txt
bash: no match: *.txt
$

Get it?

The bottom line is: Each time you're using globbing in , use failglob or nullglob to make your scripts more robust.

As a tongue in the cheek bottom line: Each time you use globbing without failglob or nullglob, God kills a kitten.

If you want more information about shopt and shell optional behaviour, please referer to The Shopt Builtin section in the manual.

Upvotes: 5

Related Questions