123456789_123456789_123456789_123456789_123456789_

Filename Matching

Filename matching is a pattern-matching feature implemented in certain Ruby methods. Each fnmatch method matches a string pattern against another string path, under the control of specified flags.

These methods operate only on strings, and do not access the file system; they are quite different from filename-globbing methods, which match patterns against string paths found in the actual file system.

Inputs to the methods:

Patterns

These are the basic elements of filename matching patterns; see the sections below for details:

Pattern Meaning Examples
Simple string. Matches itself. 'Rakefile', 'LEGAL'
'*' Matches any sequence of characters. '*.txt'
'?' Matches any single character. '?.txt'
'[abc]',
'[^abc]'
Matches a single character from a set. 'xy',
'x[^abc]y'
'[a-z]',
'[^a-z]'
Matches a single character from a range. 'xy',
'x[^0-9]y'
'' Escapes the next character. '\*', '?'

There are two other patterns that are disabled by default:

Simple String

A "simple string" is one that does not contain special filename-matching patterns; see the table above.

A simple string matches itself:

File.fnmatch('xyzzy', 'xyzzy')                     # => true
File.fnmatch('one_two_three', 'one_two_three')     # => true
File.fnmatch('123', '123')                         # => true
File.fnmatch('Form 27B/6', 'Form 27B/6')           # => true
Pathname('xyzzy').fnmatch('xyzzy')                 # => true
Pathname('one_two_three').fnmatch('one_two_three') # => true
Pathname('123').fnmatch('123')                     # => true
Pathname('Form 27B/6').fnmatch('Form 27B/6')       # => true
# Must be exact.
pattern = 'abcde'
path = 'abc'
File.fnmatch(pattern, path)                        # => false
Pathname(path).fnmatch(pattern)                    # => false

By default, the matching is case-sensitive:

pattern = 'abc'
path = 'ABC'
File.fnmatch(pattern, path)     # => false
Pathname(path).fnmatch(pattern) # => false

Case-sensitivity may be modified by flags:

By default, the alternatives pattern is disabled:

pattern = 'R{ub,foo}y'
path = 'Ruby'
File.fnmatch(pattern, path)     # => false
Pathname(path).fnmatch(pattern) # => false

It may be enabled by flag File::FNM_EXTGLOB</a>.

By default, the Windows short name pattern is disabled:

pattern ='PROGRAM~1'
path = 'Program Files'
File.fnmatch(pattern, path)     # => false
Pathname(path).fnmatch(pattern) # => false

It may be enabled by flag File::FNM_SHORTNAME</a>.

Any Sequence of Characters ('*')

The asterisk pattern ('*') matches any sequence of characters:

pattern = '*'
File.fnmatch(pattern, 'foo')     # => true
File.fnmatch(pattern, '')        # => true
File.fnmatch(pattern, 'foo')     # => true
Pathname('foo').fnmatch(pattern) # => true
Pathname('').fnmatch(pattern)    # => true
Pathname('*').fnmatch(pattern)   # => true

The pattern may be escaped:

pattern = '\*'
File.fnmatch(pattern, 'foo')     # => false
Pathname('foo').fnmatch(pattern) # => false

By default, the asterisk pattern does not match a leading period (as in a dot-file):

pattern = '*'
path = '.document'
File.fnmatch(pattern, path)     # => false
Pathname(path).fnmatch(pattern) # => false

That matching may be enabled by flag File::FNM_DOTMATCH</a>.

By default, the asterisk pattern matches across file separators:

pattern = '*.rb'
path = 'lib/test.rb'
File.fnmatch(pattern, path)     # => true
Pathname(path).fnmatch(pattern) # => true

That matching may be disabled by flag File::FNM_PATHNAME</a>.

Single Character ('?')

The question-mark pattern ('?') matches any single character:

pattern = '?'
File.fnmatch(pattern, 'f')             # => true
File.fnmatch(pattern, '')              # => false
File.fnmatch(pattern, 'foo')           # => false
Pathname('f').fnmatch(pattern)         # => true
Pathname('').fnmatch(pattern)          # => false
Pathname('foo').fnmatch(pattern)       # => false

pattern = 'foo-?.txt'
path = 'foo-1.txt'
File.fnmatch(pattern, path)     # => true
Pathname(path).fnmatch(pattern) # => true

The pattern may be escaped:

pattern = '\?'
path = 'f'
File.fnmatch(pattern, path)             # => false
Pathname(path).fnmatch(pattern)         # => false

By default, pattern '?' matches the file separator:

pattern = 'foo?bar'
path = 'foo/bar'
File.fnmatch(pattern, path)     # => true
Pathname(path).fnmatch(pattern) # => true

That matching may be disabled by flag File::FNM_PATHNAME</a>.

Single Character from a Set ('[abc]', '[^abc]')

Characters enclosed in square brackets define a set of characters, any of which matches a single character:

pattern = '[ruby]'
File.fnmatch(pattern, 'r')        # => true
File.fnmatch(pattern, 'u')        # => true
File.fnmatch(pattern, 'y')        # => true
Pathname('r').fnmatch(pattern)    # => true
Pathname('u').fnmatch(pattern)    # => true
Pathname('y').fnmatch(pattern)    # => true

# Matches a single character.
pattern = '[ruby]'
path = 'ruby'
File.fnmatch(pattern, path)     # => false
Pathname(path).fnmatch(pattern) # => false

The pattern may be escaped:

pattern = '\[ruby]'
path = 'r'
File.fnmatch(pattern, path)        # => false
Pathname(path).fnmatch(pattern)    # => false

The character set may be negated:

pattern = '[^ruby]'
File.fnmatch(pattern, 'r')     # => false
File.fnmatch(pattern, 'u')     # => false
Pathname('r').fnmatch(pattern) # => false
Pathname('u').fnmatch(pattern) # => false

Single Character from a Range ('[a-c]', '[^a-c]')

A range of characters enclosed in square brackets defines a set of characters, any of which matches a single character:

pattern = '[a-c]'
File.fnmatch(pattern, 'b')       # => true
File.fnmatch(pattern, 'd')       # => false
File.fnmatch(pattern, 'abc')     # => false
Pathname('b').fnmatch(pattern)   # => true
Pathname('d').fnmatch(pattern)   # => false
Pathname('abc').fnmatch(pattern) # => false

The pattern may be escaped:

pattern = '\[a-c]'
path = 'b'
File.fnmatch(pattern, path)       # => false
Pathname(path).fnmatch(pattern)   # => false

Multiple ranges are allowed:

pattern = 'R[t-v][a-c]y'
path = 'Ruby'
File.fnmatch(pattern, path)     # => true
Pathname(path).fnmatch(pattern) # => true

The range may be negated:

pattern = '[^a-c]'
path = 'b'
File.fnmatch(pattern, path)     # => false
Pathname(path).fnmatch(pattern) # => false

Escape ('\')

The backslash character ('\') may be used to escape any of the characters that filename matching treats as special:

path = 'b'
File.fnmatch('[a-c]', path)                         # => true
File.fnmatch('\[a-c]', path)                        # => false
File.fnmatch('[a-c\]', path)                        # => false
File.fnmatch('[a\-c]', path)                        # => false
Pathname(path).fnmatch('[a-c]')                     # => true
Pathname(path).fnmatch('\[a-c]')                    # => false
Pathname(path).fnmatch('[a-c\]')                    # => false
Pathname(path).fnmatch('[a\-c]')                    # => false

File.fnmatch('{a,b}', path, File::FNM_EXTGLOB)      # => true
File.fnmatch('\{a,b}', path, File::FNM_EXTGLOB)     # => false
File.fnmatch('{a\,b}', path, File::FNM_EXTGLOB)     # => false
File.fnmatch('{a,b\}', path, File::FNM_EXTGLOB)     # => false
Pathname(path).fnmatch('{a,b}', File::FNM_EXTGLOB)  # => true
Pathname(path).fnmatch('\{a,b}', File::FNM_EXTGLOB) # => false
Pathname(path).fnmatch('{a,b\}', File::FNM_EXTGLOB) # => false
Pathname(path).fnmatch('{a\,b}', File::FNM_EXTGLOB) # => false

Use a double-backslash to represent an ordinary backslash:

pattern = '\\\\'
path = '\\'
File.fnmatch(pattern, path)     # => true
Pathname(path).fnmatch(pattern) # => true

By default escape pattern '' is enabled; it may be disabled by flag File::FNM_NOESCAPE</a>.

Flags

Optional argument flags (defaults to 0) may be the bitwise OR of the constants File::FNM*.

These are the constants for filename-matching patterns; see the sections below for details:

Constant Meaning
File::FNM_CASEFOLD</a> Make the pattern case-insensitive.
File::FNM_DOTMATCH</a> Make pattern * match a leading period..
File::FNM_EXTGLOB</a> Enable alternatives in pattern.
File::FNM_NOESCAPE</a> Disable escaping.
File::FNM_PATHNAME</a> Make patterns '*' and '?' not match the file separator.
File::FNM_SHORTNAME</a> Enable short-name matching (Windows only).
File::FNM_SYSCASE</a> Make the pattern use OS's case sensitivity.

Constant File::FNM_CASEFOLD

By default, filename matching is case-sensitive; use constant File::FNM_CASEFOLD</a> to make the matching case-insensitive:

pattern = 'abc'
path = 'ABC'
File.fnmatch(pattern, path)                         # => false
File.fnmatch(pattern, path, File::FNM_CASEFOLD)     # => true
Pathname(path).fnmatch(pattern)                     # => false
Pathname(path).fnmatch(pattern, File::FNM_CASEFOLD) # => true

Constant File::FNM_DOTMATCH

By default, filename matching does not allow pattern '*' to match a dotfile name (i.e, a filename beginning with a dot); use constant File::FNM_DOTMATCH</a> to enable the match:

pattern = '*'
path = '.document'
File.fnmatch(pattern, path)                         # => false
File.fnmatch(pattern, path, File::FNM_DOTMATCH)     # => true
Pathname(path).fnmatch(pattern)                     # => false
Pathname(path).fnmatch(pattern, File::FNM_DOTMATCH) # => true

Constant File::FNM_EXTGLOB

By default, filename matching has the alternative notation disabled; use constant File::FNM_EXTGLOB</a> to enable it:

pattern = 'R{ub,foo}y'
path = 'Ruby'
File.fnmatch(pattern, path)                        # => false
Pathname(path).fnmatch(pattern, File::FNM_EXTGLOB) # => true
Pathname(path).fnmatch(pattern)                    # => false
Pathname(path).fnmatch(pattern, File::FNM_EXTGLOB) # => true

The alternatives pattern consists of zero or more unquoted strings, separated by commas, and enclosed in curly braces:

pattern = 'R{ub,foo,bar}y'
path = 'Ruby'
File.fnmatch(pattern, path)                        # => false
File.fnmatch(pattern, path, File::FNM_EXTGLOB)     # => true
Pathname(path).fnmatch(pattern)                    # => false
Pathname(path).fnmatch(pattern, File::FNM_EXTGLOB) # => true

Whitespace matters:

path = 'Ruby'
pattern = 'R{ub ,foo,bar}y'
File.fnmatch(pattern, path, File::FNM_EXTGLOB)     # => false
Pathname(path).fnmatch(pattern, File::FNM_EXTGLOB) # => false
pattern = 'R{ ub,foo,bar}y'
File.fnmatch(pattern, path, File::FNM_EXTGLOB)     # => false
Pathname(path).fnmatch(pattern, File::FNM_EXTGLOB) # => false

Special characters remain in force:

path = 'hello'
File.fnmatch('{*,?}', path, File::FNM_EXTGLOB)             # => true
File.fnmatch('{*ello,?}', path, File::FNM_EXTGLOB)         # => true
File.fnmatch('{*ELLO,?}', path, File::FNM_EXTGLOB)         # => false
File.fnmatch('{*ELLO,?????}', path, File::FNM_EXTGLOB)     # => true
Pathname(path).fnmatch('{*,?}', File::FNM_EXTGLOB)         # => true
Pathname(path).fnmatch('{*ello,?}', File::FNM_EXTGLOB)     # => true
Pathname(path).fnmatch('{*ELLO,?}', File::FNM_EXTGLOB)     # => false
Pathname(path).fnmatch('{*ELLO,?????}', File::FNM_EXTGLOB) # => true

Constant File::FNM_NOESCAPE

By default filename matching has escaping enabled; use constant File::FNM_NOESCAPE</a> to disable it:

pattern = '\*\?\*\*'
path = '*?**'
File.fnmatch(pattern, path)                         # => true
File.fnmatch(pattern, path, File::FNM_NOESCAPE)     # => false
Pathname(path).fnmatch(pattern)                     # => true
Pathname(path).fnmatch(pattern, File::FNM_NOESCAPE) # => false

Constant File::FNM_PATHNAME

Flag File::FNM_PATHNAME</a> affects patterns '**', '*', and '?'.

By default, the double-asterisk pattern ('**') is equivalent to pattern '*', and matches any sequence of directory-like substrings:

path = 'a/b/c'
File.fnmatch('**', path)     # => true
File.fnmatch('*', path)      # => true
Pathname(path).fnmatch('**') # => true
Pathname(path).fnmatch('*')  # => true

When flag File::FNM_PATHNAME</a> is given, the pattern matches only one component of a file path:

path = 'a/b/c'
File.fnmatch('**', path)                           # => true   # Matches 'a/b/c'.
File.fnmatch('**', path, File::FNM_PATHNAME)       # => false  # Matches only 'a'.
File.fnmatch('*/*', path, File::FNM_PATHNAME)      # => false  # Matches only 'a/b'.
File.fnmatch('**/*', path, File::FNM_PATHNAME)     # => true   # Matches 'a/b', then 'c'.
Pathname(path).fnmatch('**')                       # => true   # Matches 'a/b/c'.
Pathname(path).fnmatch('**', File::FNM_PATHNAME)   # => false  # Matches only 'a'.
Pathname(path).fnmatch('*/*', File::FNM_PATHNAME)  # => false  # Matches only 'a/b'.
Pathname(path).fnmatch('**/*', File::FNM_PATHNAME) # => true   # Matches 'a/b', then 'c'.

By default, filename matching enables pattern '*' to match at or across the file separator (File::SEPARATOR); use constant File::FNM_PATHNAME</a> to disable such matching:

File::SEPARATOR                                          # => "/"
pattern = '*.rb'
path = 'lib/test.rb'
File.fnmatch(pattern, path)                         # => true
File.fnmatch(pattern, path, File::FNM_PATHNAME)     # => false
Pathname(path).fnmatch(pattern)                     # => true
Pathname(path).fnmatch(pattern, File::FNM_PATHNAME) # => false

By default, filename matching enables pattern '?' to match at or across the file separator (File::SEPARATOR); use constant File::FNM_PATHNAME</a> to disable such matching:

pattern = 'foo?boo'
path = 'foo/boo'
File.fnmatch(pattern, path)                         # => true
File.fnmatch(pattern, path, File::FNM_PATHNAME)     # => false
Pathname(path).fnmatch(pattern)                     # => true
Pathname(path).fnmatch(pattern, File::FNM_PATHNAME) # => false

Constant File::FNM_SHORTNAME

By default, Windows shortname matching is disabled; use constant File::FNM_SHORTNAME</a> to enable it (on Windows only).

Using that constant allows patterns to match short names in filename matching on Windows, which can be useful for compatibility with legacy applications that rely on these short names; see 8.3 filename. This feature helps ensure that file operations work correctly even when dealing with files that have long names.

File::FNM_SHORTNAME.zero? # => false  # On Windows, not zero; may be enabled.
File::FNM_SHORTNAME.zero? # => true   # Elsewhere, always zero; may not be enabled.
pattern = 'PROGRAM~1'
path = 'Program Files'
File.fnmatch(pattern, path)                          # => false
Pathname(path).fnmatch(pattern)                      # => false
# These will return true if and only if on Windows and short name 'PROGRAM~1' exists.
File.fnmatch(pattern, path, File::FNM_SHORTNAME)     # => true
Pathname(path).fnmatch(pattern, File::FNM_SHORTNAME) # => true

Constant File::FNM_SYSCASE

By default, filename matching uses Ruby's own case-sensitivity rules; use constant File::FNM_SYSCASE</a> to use the case-sensitivity rules of the underlying file system:

File::FNM_SYSCASE.zero? # => false  # On Windows, not zero; may be enabled.
File::FNM_SYSCASE.zero? # => true   # Elsewhere, always zero; may not be enabled.
pattern = 'abc'
path = 'ABC'
File.fnmatch(pattern, path)                        # => false  # Ruby; case-sensitive.
File.fnmatch(pattern, path, File::FNM_SYSCASE)     # => true   # Windows; case-insensitive.
File.fnmatch(pattern, path, File::FNM_SYSCASE)     # => false  # Linux; case-sensitive.
Pathname(path).fnmatch(pattern)                    # => false  # Ruby; case-sensitive.
Pathname(path).fnmatch(pattern, File::FNM_SYSCASE) # => true   # Windows; case-insensitive.
Pathname(path).fnmatch(pattern, File::FNM_SYSCASE) # => false  # Linux; case-sensitive.