Paths and directories
Pathname
Pathname wraps a path in an object that knows the operations. + appends a name with the right separator, and / joins two paths:
ruby -e 'require "pathname"; p Pathname("a") + "b.txt"'
#<Pathname:a/b.txt>
The object has dirname, basename, extname, exist?, read and write, so a method can take a path and call what it needs without converting to a string first:
ruby -e 'require "pathname"; p Pathname("a/b.txt").dirname; p Pathname("a/b.txt").basename.to_s'
#<Pathname:a>
"b.txt"
File.join
When a plain string is enough, File.join puts one separator between the parts and removes doubled ones:
ruby -e 'p File.join("a", "b")'
"a/b"
Do not build a path with "a" + "/" + "b": the separator is not / on Windows, and a path that starts with one absolute part swallows the rest. File.join and Pathname#+ are the two forms that work everywhere.
Dir.glob
Dir.glob matches a pattern against the names in a directory. In a folder with two text files and a Markdown file, it sorts the result, so two runs on the same folder print the same list:
ruby -e 'p Dir.glob("*.txt")'
["a.txt", "b.txt"]
The pattern speaks the shell language: * for one level, **/ for any depth, ? for one character and {a,b} for a choice. A recursive pattern is the shortest way to find files in a tree:
Dir.glob("**/*.txt").sort.each do |path|
puts path
end
ruby glob_demo.rb
a.txt
b.txt
A glob returns names, not contents, and it does not look inside hidden directories unless the pattern starts with a dot. A dotfile that a script must find needs Dir.glob(".*") or File::FNM_DOTMATCH.
The parts of a path
Three class methods answer the questions a file name asks, and each one works on a string:
ruby -e 'p File.basename("/x/y/report.txt"); p File.dirname(File.join("a", "b.txt")); p File.extname("a.tar.gz")'
"report.txt"
"a"
".gz"
extname returns the last extension only, and it includes the dot. A project that treats .tar.gz as one suffix strips it with its own rule, not with extname.
Temporary directories
A test or a conversion needs a folder it can fill and throw away. Dir.mktmpdir creates one in the system temporary area and removes it when the block ends:
ruby -e 'require "tmpdir"; Dir.mktmpdir { |d| p File.directory?(d) }'
true
Lesson 115 covers the cleanup guarantee. Pathname plus mktmpdir is the pair for a test that writes real files: the paths are within the temporary folder, and nothing a developer has on disk is touched.
What to watch for
A relative path in a script resolves against the working directory, not
against the script file. ruby bin/report.rb run
from the project root and from bin/ sees different
files. Use __dir__ to anchor a path to the code:
File.join(__dir__, "data.csv").
A path with a space is fine as a string and a problem in a shell command.
Pass the path as a separate argument to
system rather than building one command line.
Next: Binary files and streams
Article author: Arthur Isaev