fnmatch
Sometimes you may wish to know if one or more files simply exist by a given pathname.
This is usually trivial, for example:
1
if [ -e /some/dir/foo ]; then
2
echo "Found foo in /some/dir"
3
fi
Copied!
Returns success if a file, directory, or other filesystem component exists at /some/dir/foo.
However, if you want to test for existence by pattern, it becomes a little more difficult. Since pathname expansion only occurs with one or more matches (leaving the original pattern unexpanded for zero matches) you have to retest existence after expansion. This is commonly done by placing the pattern in the argument space of a for-loop and testing at the onset that the first path given exists.
1
found=
2
for foo in /some/dir/*foo*; do
3
#
4
# foo is /some/dir/*foo* if no matches found
5
# foo is expanded to matches if found
6
#
7
if [ ! -e "$foo" ]; then
8
#
9
# The pattern was not expanded, skip remaining lines
10
# Since only one pattern was given above,
11
# this continue is analogous to a break before found=1
12
#
13
continue
14
fi
15
# If we reach here, at least one match was expanded
16
found=1
17
break
18
done
19
if [ "$found" ]; then
20
echo "Found *foo* in /some/dir"
21
fi
Copied!
Another method involves passing the pattern as an argument to the set built-in to generate expansions, however this is not recommended because you can overflow the Operating System's maximum number of arguments leading to an error of "Too many arguments" upon pattern expansion.
1
set -- /some/dir/*foo*
2
# WARNING: Potential "Too many arguments" if expansion is many
3
if [ -e "$1" ]; then
4
echo "Found *foo* in /some/dir"
5
fi
Copied!
A solution exists that is both simple and elegant. An fnmatch function:
1
1 #!/bin/sh
2
2 fnmatch()
3
3 {
4
4 [ -e "$1" ]
5
5 }
Copied!
The pattern is expanded before passing arguments to fnmatch which means it need only test if the first argument exists. If the first argument exists, an expansion was performed, otherwise the first argument remains a path to a non-existent, un-expandable pattern.
We can now use fnmatch as a method of testing whether a pathname containing patterns can successfully be expanded to one or more existing filesystem element(s):
1
if fnmatch /some/dir/*foo*; then
2
echo "Found *foo* in /some/dir"
3
fi
Copied!
This method can be used to improve the efficiency of loops when you expect many items to be expanded. For example, instead of testing for existence inside a loop for cases where the pattern is unexpanded, use fnmatch as a condition for looping.
1
fnmatch /some/dir/*foo* && for foo in /some/dir/*foo*; do
2
echo "foo=[$foo]"
3
done
Copied!
Copy link