Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to skip the for loop when there are no matching files?

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)?

like image 341
Jav Avatar asked Oct 08 '14 11:10

Jav


2 Answers

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*
like image 161
fedorqui 'SO stop harming' Avatar answered Nov 15 '22 21:11

fedorqui 'SO stop harming'


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.

like image 37
chepner Avatar answered Nov 15 '22 22:11

chepner