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:
-
For singleton method
File.fnmatch, the path is the string argumentpath; for instance methodPathname#fnmatch, the path is the stringself.to_s. -
The
flagsargument is an integer value that may be defined by constants; see Flags.
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]‘`,<br>`’‘` | Matches a single character from a set. | ‘’x[abc]y’‘,<br>`’xy’‘ |
| ‘’[a-z]‘’,<br>‘’‘` | Matches a single character from a range. | ‘’x[0-9]y’‘,<br>`’xy’‘ |
| ‘”` | Escapes the next character. | ‘’*‘`, `’?‘` |
There are two other patterns that are disabled by default:
-
Directory-like substring (
'**'); see {File::FNM_PATHNAME} below. -
Alternatives (
'{ , }'); see {File::FNM_EXTGLOB} below.
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}.
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}.
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}.
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}.
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}.
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}.
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`](constant-filefnmcasefold) | Make the pattern case-insensitive. |
| [‘File::FNM_DOTMATCH`](constant-filefnmdotmatch) | Make pattern ‘*` match a leading period.. |
| [‘File::FNM_EXTGLOB`](constant-filefnmextglob) | Enable alternatives in pattern. |
| [‘File::FNM_NOESCAPE`](constant-filefnmnoescape) | Disable escaping. |
| [‘File::FNM_PATHNAME`](constant-filefnmpathname) | Make patterns ‘’*‘` and `’?‘` not match the file separator. |
| [‘File::FNM_SHORTNAME`](constant-filefnmshortname) | Enable short-name matching (Windows only). |
| [‘File::FNM_SYSCASE`](constant-filefnmsyscase) | Make the pattern use OS’s case sensitivity. |
Constant File::FNM_CASEFOLD¶ ↑
By default, filename matching is case-sensitive; use constant {File::FNM_CASEFOLD} 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} 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} 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} 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} 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} 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} 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} 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} 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} 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.