17

When I loop through all the files starting by foo I do

for f in foo* ; do echo "result = $f" ; done

The problem is when no file start by foo I get:

result = foo*

Meaning that the loop is executed once, even if no file start by foo.

How is this possible? How can I loop through all files (and not loop at all if there is no file)?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Jav
  • 1,445
  • 1
  • 18
  • 47
  • Possible duplicate of [bash loop over file mask](https://stackoverflow.com/questions/26031736/bash-loop-over-file-mask) – cnst Jun 15 '19 at 06:47

2 Answers2

18

You can stop this behaviour by setting nullglob:

shopt -s nullglob

From the linked page:

nullglob is a Bash shell option which modifies [[glob]] expansion such that patterns that match no files expand to zero arguments, rather than to themselves.

You can remove this setting with -u (unset, whereas s is for set):

shopt -u nullglob

Test

$ touch foo1 foo2 foo3
$ for file in foo*; do echo "$file"; done
foo1
foo2
foo3
$ rm foo*

Let's see:

$ for file in foo*; do echo "$file"; done
foo*

Setting nullglob:

$ shopt -s nullglob
$ for file in foo*; do echo "$file"; done
$

And then we disable the behaviour:

$ shopt -u nullglob
$ for file in foo*; do echo "$file"; done
foo*
fedorqui
  • 275,237
  • 103
  • 548
  • 598
12

The standard way to do this (if you can't or don't want to use nullglob) is to simply check if the file exists.

for file in foo*; do
    [ -f "$file" ] || continue
    ...
done

The overhead of checking each value of $file is necessary because if $file expands to foo*, you don't yet know if there actually was a file named foo* (because it matches the pattern) or if the pattern failed to match and expanded to itself. Using nullglob, of course, removes that ambiguity because a failed expansion produces no arguments and the loop itself never executes the body.

fedorqui
  • 275,237
  • 103
  • 548
  • 598
chepner
  • 497,756
  • 71
  • 530
  • 681