Skip to content
On this page

Preview Ruby 4.1 is a preview. It can change before the final release. Switch to Ruby 4.0 →

guide · Ruby 4.1

Filename Globbing

Filename globbing is a pattern-matching feature implemented in certain Ruby methods:

Each glob method selects filesystem entries (file and directory names) that match certain patterns, under the control of keyword arguments base and flags; the selected entries may be sorted, according to keyword argument sort.

These filename-globbing methods are quite different from filename-matching methods, which match patterns against string paths, and do not access the filesystem.

Inputs to the filename-globbing methods:

  • The argument patterns is a string or an array of strings, which are not Regexp objects; see Patterns.

  • Keyword argument base specifies the entry in the filesystem where searching is to begin; see base.

  • Keyword argument flags specifies an integer value that may be defined by constants; see flags. This argument is not available in method Dir[], for which the flags value is zero.

  • Keyword argument sort specifies whether the returned array is to be sorted. see sort.

Their return values:

  • Method Dir[] returns an array of the selected string entries.

  • Method Dir.glob with no block returns an array of the selected string entries.

  • Method Dir.glob with a block calls the block with each selected string entry and returns nil.

  • Method Pathname#glob with no block returns an array of Pathname objects each based on a selected string entry.

  • Method Pathname#glob with a block calls the block with each pathname based on a selected string entry.

Examples:

Example
Dir['*'].take(3)
# => ["BSDL", "CONTRIBUTING.md", "COPYING"]
Dir['*.rb', '*.h'].take(3)
# => ["KNOWNBUGS.rb", "array.rb", "ast.rb"]
Dir.glob('*').take(3)
# => ["BSDL", "CONTRIBUTING.md", "COPYING"]
Dir.glob(['*.rb', '*.h']).take(3)
# => ["KNOWNBUGS.rb", "array.rb", "ast.rb"]
Dir.glob('*.rb') {|entry_name| puts entry_name } # => nil
Pathname('.').glob('*').take(3)
# => [#<Pathname:BSDL>, #<Pathname:CONTRIBUTING.md>, #<Pathname:COPYING>]
a = []
Pathname('.').glob('*') {|pn| a << pn if pn.to_s.end_with?('.c') } # => nil
a.take(3) # => [#<Pathname:addr2line.c>, #<Pathname:array.c>, #<Pathname:ast.c>]

The examples below for Pathname#glob do not give blocks.

Patterns

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

Pattern Meaning Examples
Simple string. Matches itself. 'LEGAL'
'*' Matches any sequence of characters. '*.txt'
'?' Matches any single character. '?.txt'
'[abc]',
'[^abc]'
Matches a single character from a set. 'x[abc]y',
'x[^abc]y'
'[a-z]‘,
'[^a-z]'
Matches a single character from a range. 'x[0-9]y',
'x[^0-9]y'
'{ , }' Matches alternatives. '{abc,def}'
'**' Matches directories recursively. '**/test.rb'
'***' Matches recursively, following directory symlinks. '***/test.rb'
'\' Escapes the next character. '\*', '\?'

Patterns

Simple String

A “simple string” is one that does not contain special filename-globbing patterns; see the table above.

A simple string matches itself:

Example
Dir.glob('LEGAL')           # => ["LEGAL"]
Dir.glob('LEGA')            # => []  # Must be exact.
Dir.glob('legal')           # => []  # Case-sensitive.
Pathname('.').glob('LEGAL') # => [#<Pathname:LEGAL>]
Pathname('.').glob('LEGA')  # => []  # Must be exact.
Pathname('.').glob('legal') # => []  # Case-sensitive.

Note that case-sensitivity may not be modified by flags.

By default, the Windows short name pattern is disabled:

Example
Dir.glob('PROGRAM~1')           # => []
Pathname('.').glob('PROGRAM~1') # => []

It may be enabled by flag File::FNM_SHORTNAME.

Any Sequence of Characters ('*')

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

Example
Dir.glob('*').take(3) # => ["BSDL", "CONTRIBUTING.md", "COPYING"]
Pathname('.').glob('*').take(3)
# =>
# #<Pathname:BSDL>,
#     #<Pathname:CONTRIBUTING.md>,
#     #<Pathname:COPYING>]

The pattern may be escaped:

Example
Dir.glob('\*')           # => []
Pathname('.').glob('\*') # => []

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

Example
Dir.glob('*').select {|entry| entry.start_with?('.') }           # => []
Pathname('.').glob('*') .select {|pn| pn.to_s.start_with?('.') } # => []

That matching may be enabled by flag File::FNM_DOTMATCH.

The asterisk pattern does not match across file separators:

Example
Dir.glob('*.rb').select {|entry| entry.include?('/') }           # => []
Pathname('.').glob('*.rb') .select {|pn| pn.to_s.include?('/') } # => []

Therefore flag File::FNM_PATHNAME does not affect the pattern.

Single Character ('?')

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

Example
Dir.glob('???') # => ["GPL", "bin", "doc", "enc", "ext", "jit", "lib", "man"]
Dir.glob('??')  # => ["gc"]                     # Only one entry with a 2-character name.
Dir.glob('?')   # => []                         # No entries with a 1-character name.
Dir.glob('\?')  # => []                         # No entries containing character '?'.
Pathname('.').glob('???').take(3) # => [#<Pathname:GPL>, #<Pathname:bin>, #<Pathname:doc>]
Pathname('.').glob('??') # => [#<Pathname:gc>]  # Only one entry with a 2-character name.
Pathname('.').glob('?')  # => []                # No entries with a 1-character name.
Pathname('.').glob('\?') # => []                # No entries containing character '?'.

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

Example
Dir.glob(".???")                                                   # => [".git"]
Dir.glob("????").select {|entry| entry.start_with?('.') }          # => []
Pathname('.').glob('.???')                                         # => [#<Pathname:.git>]
Pathname('.').glob('????').select {|pn| pn.to_s.start_with?('.') } # => []

That matching may be enabled by flag File::FNM_DOTMATCH.

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

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

Example
Dir.glob('[efgh][abcd]')           # => ["gc"]
Pathname('.').glob('[efgh][abcd]') # => [#<Pathname:gc>]

The pattern may be escaped:

Example
Dir.glob('\[efgh][abcd]')           # => []
Pathname('.').glob('\[efgh][abcd]') # => []

The character set may be negated:

Example
Dir.glob('[^abcd][^efgh]')           # => ["gc"]
Pathname('.').glob('[^abcd][^efgh]') # => [#<Pathname:gc>]

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:

Example
Dir.glob('[k-m][h-j][a-c]')           # => ["lib"]
Pathname('.').glob('[k-m][h-j][a-c]') # => [#<Pathname:lib>]

The pattern may be escaped:

Example
Dir.glob('\[k-m][h-j][a-c]')           # => []
Pathname('.').glob('\[k-m][h-j][a-c]') # => []

The range may be negated:

Example
Dir.glob('[^k-m][h-j][a-c]')           # => []
Dir.glob('[^a-c][^k-m][^h-j]').take(3) # => ["GPL", "doc", "enc"]
Pathname('.').glob('[^k-m][h-j][a-c]') # => []
Pathname('.').glob('[^a-c][^k-m][^h-j]').take(3)
# => [#<Pathname:GPL>, #<Pathname:doc>, #<Pathname:enc>]

Alternatives ('{ , }')

The alternatives pattern consists of comma-separated strings enclosed in curly braces:

Example
Dir.glob('{k,L,R}*').take(3)  # => ["kernel.rb", "LEGAL", "README.ja.md"]
Dir.glob('{R,L,k}*').take(3)  # => ["README.ja.md", "README.md", "LEGAL"]
Pathname('.').glob('{k,L,R}*').take(3)
# # =>
# [#<Pathname:kernel.rb>,
#     #<Pathname:LEGAL>,
#     #<Pathname:README.ja.md>]
Pathname('.').glob('{R,L,k}*').take(3)
# # =>
# [#<Pathname:README.ja.md>,
#     #<Pathname:README.md>,
#     #<Pathname:LEGAL>]

Whitespace matters:

Example
Dir.glob('{k ,L,R}*') # => ["LEGAL", "README.ja.md", "README.md"]
Pathname('.').glob('{k ,L,R}*')
# # =>
# [#<Pathname:LEGAL>,
#     #<Pathname:README.ja.md>,
#     #<Pathname:README.md>]

Recursive Directory Matching ('**')

The double-asterisk pattern ('**') matches directories recursively:

Example
# Find all entries everywhere ending with '.ja'.
Dir.glob('**/*.ja') # => ["COPYING.ja", "doc/pty/README.expect.ja", "doc/pty/README.ja"]
Pathname('.').glob('**/*.ja')
# # =>
# [#<Pathname:COPYING.ja>,
#     #<Pathname:doc/pty/README.expect.ja>,
#     #<Pathname:doc/pty/README.ja>]

# Find all entries everywhere ending with '.rb'.
Dir.glob('**/*.rb').size           # => 7527
Dir.glob('**/*.rb').take(3)        # => ["KNOWNBUGS.rb", "array.rb", "ast.rb"]
Pathname('.').glob('**/*.rb').size # => 7527
Pathname('.').glob('**/*.rb').take(3)
# # =>
# [#<Pathname:KNOWNBUGS.rb>,
#     #<Pathname:array.rb>,
#     #<Pathname:ast.rb>]

# Find all entries in directory 'lib' ending with `.rb'.
Dir.glob('lib/**/*.rb').size           # => 621
Dir.glob('lib/**/*.rb').take(3)
# # =>
# ["lib/English.rb",
#  "lib/bundled_gems.rb",
#  "lib/bundler/build_metadata.rb"]
Pathname('.').glob('lib/**/*.rb').size # => 621
Pathname('.').glob('lib/**/*.rb').take(3)
# # =>
# [#<Pathname:lib/English.rb>,
#     #<Pathname:lib/bundled_gems.rb>,
#     #<Pathname:lib/bundler/build_metadata.rb>]

# Find all entries in directory 'test/ruby' ending with '.rb'.
Dir.glob('test/ruby/**/*.rb').size           # => 200
Dir.glob('test/ruby/**/*.rb').take(3)
# # =>
# ["test/ruby/allpairs.rb",
#  "test/ruby/beginmainend.rb",
#  "test/ruby/box/a.1_1_0.rb"]
Pathname('.').glob('test/ruby/**/*.rb').size # => 200
Pathname('.').glob('test/ruby/**/*.rb').take(3)
# # =>
# [#<Pathname:test/ruby/allpairs.rb>,
#     #<Pathname:test/ruby/beginmainend.rb>,
#     #<Pathname:test/ruby/box/a.1_1_0.rb>]

The double-asterisk pattern matches directories recursively only when followed by the slash character '/'; otherwise it is equivalent to pattern '*':

Example
Dir.glob('**') == Dir.glob('*') # => true

The triple-asterisk pattern ('***') also matches directories recursively, following symbolic links to directories without limiting their number. To avoid cycles, recursive traversal stops at a symbolic link to an ancestor directory in the current traversal path. The link itself can still match. Different paths to the same directory are searched independently. The double-asterisk pattern does not follow symbolic links during recursive matching.

Like '**', '***' is recursive only when it is an entire path component followed by '/'. Otherwise, its asterisks are ordinary wildcards:

ruby Dir.glob('***/test.rb') # Also searches through directory symlinks. Dir.glob('***') == Dir.glob('*') # => true Dir.glob('foo***/bar') == Dir.glob('foo*/bar') # => true

A pattern ending with '/' matches directory names only; each matched name ends with '/':

Example
# Find the directories at the top level.
Dir.glob('*/').take(3)  # => ["basictest/", "benchmark/", "bin/"]

# Find all directories everywhere.
Dir.glob('**/').take(3) # => ["basictest/", "benchmark/", "benchmark/gc/"]

The pattern may be escaped:

Example
Dir.glob('\**/*.rb')           # => []
Pathname('.').glob('\**/*.rb') # => []

Escape ('\')

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

Example
Dir.glob('\*')                         # => []
Dir.glob('\?')                         # => []
Dir.glob('\[efgh][abcd]')              # => []
Dir.glob('\[k-m][h-j][a-c]')           # => []
Dir.glob('\**/*.rb')                   # => []
Pathname('.').glob('\*')               # => []
Pathname('.').glob('\?')               # => []
Pathname('.').glob('\[efgh][abcd]')    # => []
Pathname('.').glob('\[k-m][h-j][a-c]') # => []
Pathname('.').glob('\**/*.rb')         # => []

Note that the backslash escapes the following character on Windows as well, and therefore cannot be used as a path separator in the pattern; Dir.glob('C:\Users*') matches nothing. Write the pattern with forward slashes instead:

Example
Dir.glob('C:/Users/*')

A Windows path taken from an external source (such as an environment variable) may be converted for use as a pattern:

Example
pattern = path.tr('\\', '/')

Keyword Arguments

Keyword Value Default Meaning
base String path. '.' Root for searching.
flags Logical OR of constants. 0 Modify globbing behavior.
sort true or false true Whether returned array is to be sorted.

base

Optional keyword argument base (defaults to '.') specifies (for Dir.glob) where in the filesystem the searching is to begin; the argument is ignored for Pathname#glob, whose “base” is its string path:

Example
# Default base '.'.
Dir.glob('*').take(3)
# => ["BSDL", "CONTRIBUTING.md", "COPYING"]
Dir.glob('*', base: 'lib').take(3)
# => ["English.gemspec", "English.rb", "bundled_gems.rb"]
Dir.glob('*', base: 'lib/net').take(3)
# => ["http", "http.rb", "https.rb"]
Pathname('.').glob('*').take(3)
# => [#<Pathname:BSDL>, #<Pathname:CONTRIBUTING.md>, #<Pathname:COPYING>]
Pathname('lib').glob('*').take(3)
# => [#<Pathname:lib/English.gemspec>, #<Pathname:lib/English.rb>, #<Pathname:lib/bundled_gems.rb>]
Pathname('lib/net').glob('*').take(3)
# => [#<Pathname:lib/net/http>, #<Pathname:lib/net/http.rb>, #<Pathname:lib/net/https.rb>]

Note that the base directory is not prepended to the entry names in the result.

flags

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

Example
Dir.glob('*', flags: File::FNM_DOTMATCH | File::FNM_NOESCAPE)

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

Constant Meaning
File::FNM_DOTMATCH Make pattern '*' match a leading period.
File::FNM_NOESCAPE Disable escaping.
File::FNM_SHORTNAME Enable short-name matching (Windows only).

These constants do not affect filename globbing:

Constant File::FNM_DOTMATCH

By default, filename globbing does not allow patterns '*' and '?' to match a dotfile name (i.e, an entry name beginning with a dot); use constant File::FNM_DOTMATCH to enable the match:

Example
Dir.glob('*').size                                      # => 241
Dir.glob('*', flags: File::FNM_DOTMATCH).size           # => 256
Dir.glob('*', flags: File::FNM_DOTMATCH).take(3)
# => [".", ".dir-locals.el", ".document"]
Pathname('.').glob('*').size                            # => 241
Pathname('.').glob('*', flags: File::FNM_DOTMATCH).size # => 256
Pathname('.').glob('*', flags: File::FNM_DOTMATCH).take(3)
# => [#<Pathname:.>, #<Pathname:.dir-locals.el>, #<Pathname:.document>]

Constant File::FNM_NOESCAPE

By default filename globbing has escaping enabled; use constant File::FNM_NOESCAPE to disable it:

Example
Dir.glob('*').size                                # => 241
Dir.glob('\*').size                               # => 0
Dir.glob('\*', File::FNM_NOESCAPE).size           # => 0
Pathname('.').glob('*').size                      # => 241
Pathname('.').glob('\*').size                     # => 0
Pathname('.').glob('\*', File::FNM_NOESCAPE).size # => 0

Note that on Windows this flag does not make the backslash usable as a path separator in the pattern; the backslash is then matched as an ordinary character, which cannot occur in an entry name.

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 globbing 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.

sort

Optional keyword argument sort (defaults to 'true') specifies whether the returned array is to be sorted:

Example
Dir.glob('*').take(3)              # => ["BSDL", "CONTRIBUTING.md", "COPYING"]
Dir.glob('*', sort: false).take(3) # => ["gc.rb", "yjit.rb", "iseq.h"]
Pathname('.').glob('*').take(3)
# => [#<Pathname:BSDL>, #<Pathname:CONTRIBUTING.md>, #<Pathname:COPYING>]
Pathname('.').glob('*', sort: false).take(3)
# => [#<Pathname:gc.rb>, #<Pathname:yjit.rb>, #<Pathname:iseq.h>]

From doc/file/filename_globbing.md in Ruby 4.1. View on GitHub ↗ docs.ruby-lang.org ↗