Loop through all the files with a specific extension

No fancy tricks needed:

for i in *.java; do
    [ -f "$i" ] || break
    ...
done

The guard ensures that if there are no matching files, the loop will exit without trying to process a non-existent file name *.java.

In bash (or shells supporting something similar), you can use the nullglob option
to simply ignore a failed match and not enter the body of the loop.

shopt -s nullglob
for i in *.java; do
    ...
done

Some more detail on the break-vs-continue discussion in the comments. I consider it somewhat out of scope whether you use break or continue, because what the first loop is trying to do is distinguish between two cases:

  1. *.java had no matches, and so is treated as literal text.
  2. *.java had at least one match, and that match might have included an entry named *.java.

In case #1, break is fine, because there are no other values of $i forthcoming, and break and continue would be equivalent (though I find break more explicit; you’re exiting the loop, not just waiting for the loop to exit passively).

In case #2, you still have to do whatever filtering is necessary on any possible matches. As such, the choice of break or continue is less relevant than which test (-f, -d, -e, etc) you apply to $i, which IMO is the wrong way to determine if you entered the loop “incorrectly” in the first place.

That is, I don’t want to be in the position of examining the value of $i at all in case #1, and in case #2 what you do with the value has more to do with your business logic for each file, rather than the logic of selecting files to process in the first place. I would prefer to leave that logic to the individual user, rather than express one choice or the other in the question.


As an aside, zsh provides a way to do this kind of filtering in the glob itself. You can match only regular files ending with .java (and disable the default behavior of treating unmatched patterns as an error, rather than as literal text) with

for f in *.java(.N); do
  ...
done

With the above, you are guaranteed that if you reach the body of the loop, then $f expands to the name of a regular file. The . makes *.java match only regular files, and the N causes a failed match to expand to nothing instead of producing an error.

There are also other such glob qualifiers for doing all sorts of filtering on filename expansions. (I like to joke that zsh‘s glob expansion replaces the need to use find at all.)

Leave a Comment