class Pathname
A Pathname object stores a string:
pn = Pathname('README.md') # => #<Pathname:README.md> pn.to_s # => "README.md"
The string is usually an actual or potential path to an entry in the filesystem:
Pathname('lib') # => #<Pathname:lib> # Relative path. Pathname('/usr/lib') # => #<Pathname:/usr/lib> # Absolute path. Pathname('nosuch/foo') # => #<Pathname:nosuch/foo> # Need not exist. Pathname('!@#$%^&*()') # => #<Pathname:!@#$%^&*()> # Need not be a valid path.
Through its many instance methods, the pathname object provides a consistent and convenient interface to numerous methods in other classes and modules:
Advantages of using a pathname instead of these others:
-
You don’t have to know which class or module has which methods.
-
You don’t have to keep typing the class name and path variable.
Without pathnames:
filepath = 'README.md' File.exist?(filepath) # => true File.file?(filepath) # => true File.writable?(filepath) # => true dirpath = 'tempdir' Dir.mkdir(dirpath) Dir.rmdir(dirpath)
With pathnames:
pn = Pathname('README.md') pn.exist? # => true pn.file? # => true pn.writable? # => true pn = Pathname('tempdir') pn.mkdir pn.rmdir
In addition to its wrapper methods, Pathname has certain “core” methods that are not simple wrappers:
-
+
Of particular interest may be cleanpath, which reduces a “noisy” path to a simpler form.
Some pathname methods return pathnames, which may be chained:
Pathname("/usr") .join("local") .join("bin") .exist? # => true
A Pathname object is immutable (except for method freeze).
About the Examples
Many examples here use these variables:
# English text with newlines. text = <<~EOT First line Second line Fourth line Fifth line EOT # Japanese text. japanese = 'こんにちは' # Binary data. data = "\u9990\u9991\u9992\u9993\u9994" # Text file. File.write('t.txt', text) # File with Japanese text. File.write('t.ja', japanese) # File with binary data. f = File.new('t.dat', 'wb:UTF-16') f.write(data) f.close
What’s Here
First, what’s elsewhere. Class Pathname:
-
Inherits from class Object.
Here, class Pathname provides methods that are useful for:
Creating
-
::getwd(aliased as::pwd): Returns a new Pathname object containing the path to the current working directory. -
::mktmpdir: Creates a Pathname object containing the path to a new temporary directory; returns it or passes it to the block. -
::new: Returns a new Pathname object based on the given path.
Querying
-
::glob: Selects filesystem entries; creates a pathname for each; returns them or passes them to the block. -
absolute?: Returns whetherselfcontains an absolute path. -
blockdev?: Returns whether the entry at the path inselfis a block device. (i.e., a direct-access device). -
chardev?: Returns whether entry at the path inselfis a character device. (i.e., a sequential-access device). -
directory?: Returns whether the entry at the path inselfis a directory. -
empty?: Returns whether the entry at the path inselfexists and is empty. -
entries: Returns an array of pathnames, one for each entry in the directory at the path inself. -
executable?: Returns whether the entry at the path inselfis executable. -
executable_real?: Returns whether the entry at the path inselfis executable by the real user and group id of the current process. -
exist?: Returns whether the entry at the path inselfexists. -
extname: Returns the filename extension ofself. -
file?: Returns whether the entry at the path inselfexists and is a regular file. -
find: Performs a depth-first traversal of the path inself; calls the block with each found path. -
fnmatch: Returns whether the given pattern matches against the path inself. -
ftype: Returns the string type of the entry at the path inself. -
glob: Finds entries and returns or yields pathnames. -
grpowned?: Returns whether the entry for the path inselfexists, and the effective group id of the calling process is the owner of the entry. -
lstat: Returns aFile::Statentry for the path inself, but does not follow symbolic links, and therefore returns the stat object for the entry at the path inself. -
mountpoint?: Returns whether the entry at the path inselfis a mountpoint. -
owned?: Returns whether the entry at the path inselfexists and is owned by the user of the current process. -
parent: Returns a new pathname containing the path to the parent directory of the entry inself. -
pipe?: Returns whether the entry at the path inselfis a pipe. -
readable?: Returns whether the entry at the path inselfis readable by the owner and group of the current process. -
readable_real?: Like readable?, but checks against the real user and group ids instead of the effective ids. -
relative?: Returns whetherselfcontains a relative path. -
root?: Returns whether the entry at the path inselfis a root directory. -
setgid?: Returns whether the setgid bit is set in the permissions for the entry at the path inself. -
setuid?: Returns whether the setuid bit is set in the permissions for the entry at the path inself. -
size: Returns the size of the entry at the path inself. -
size?: Returns the size of the entry at the path inselfif the entry exists and has non-zero size;nilotherwise. -
socket?: Returns whether the entry at the path inselfis a socket. -
split: Returns a 2-element array containing thedirnameandbasenameof the path inself. -
stat: Returns aFile::Statobject for the entry at the path inself. -
sticky?: Returns whether the sticky bit is set for the entry at the path inself. -
symlink?: Returns whether the entry at the path inselfis a symbolic link. -
world_readable?: If the entry at the path inselfis readable by others, returns the integer permissions for the entry. -
world_writable?: If the entry at the path inselfis writable by others, returns the integer permissions for the entry. -
writable?: Returns whether the file entry at the path inselfis writable by the effective user and group id of this process. -
writable_real?: Likewritable?, but checks against the real user and group ids instead of the effective ids. -
zero?: Returns whether the entry at the path inselfexists and has size zero.
Comparing
-
<=>: Compares the contents ofselfand the given object as strings. -
==(aliased as===andeql?): Returns whether the paths inselfand the given object are equal.
Analyzing
-
ascend: Yieldsselfto the block, then yields a new pathname for each successive dirname in the path inself. -
basename: Returns all or part of the last component of the path inself. -
children: Returns an array of pathnames, each containing the path to a child of the entry at the path inself. -
descend: Yields to the block a new pathname for each successive dirname in the path inself. -
dirname: Returns all but the last component of the path inself. -
each_filename: Calls the block with each component of the path inself.
Converting
-
+(aliased as/): Returns a new Pathname object based on the content ofselfand the given object. -
cleanpath: Returns a new pathname, “cleaned” of unnecessary separators, single-dot entries, and double-dot entries. -
expand_path: Returns a new pathname containing the absolute path forself. -
join: Joins the string-converted given objects to the string path inself; returns the joined string as a pathname. -
readlink: Returns a new pathname containing the path to the entry referenced byself. -
realdirpath: Returns a new pathname containing the real (absolute) path of the directory entry at the path inself. -
realpath: Returns a new pathname containing the real (absolute) path of the file entry at the path inself. -
relative_path_from: Returns a new pathname containing the relative path from the given directory path to the path inself. -
sub: Returns a new pathname whose path is the path inself, after specified substitutions. -
sub_ext: Returns a new pathname whose path is the path inself, after specified changes. -
to_s(aliased asto_path): Returns a copy of the path inself.
Ownership and Permissions
-
chmod: Changes the mode (i.e., permissions) of the entry at the path inself. -
chown: Changes the owner and group of an entry (directory or file). -
lchmod: Likechmod, but does not follow symbolic links, and therefore changes the mode of the entry at the path inself. -
lchown: Likechown, but does not follow symbolic links, and therefore changes the ownership of the entry at the path inself.
Reading and Writing
-
binread: Behaves likeread, except that the file is opened in binary mode with ASCII-8BIT encoding. -
binwrite: Behaves likewrite, except that the file is opened in binary mode with ASCII-8BIT encoding. -
make_link: Creates a new entry at the path inselffor the existing entry at path using a hard link. -
make_symlink: Creates a symbolic link at the path inselfto the entry at path. -
mkdir: Creates a directory entry at the path inself. -
mkpath: Creates a directory entry at the path inself; creates intermediate directories as needed. -
open: Opens the file at the entry inselffor reading or writing. -
opendir: Creates and opens aDirobject for the directory at the path inself; either returns theDirobject or calls the block with it. -
read: Reads and returns some or all of the content of the file entry at the path inself. -
readlines: Returns an array of all lines read from file entry at the path inself. -
rename: Renames the entry at the path inself. -
rmdir: Deletes the directory entry at the path inself. -
rmtree: Deletes the entire filetree at the path inself. -
sysopen: Opens the file at the path inself; returns the integer file descriptor. -
truncate: Adjusts the size of file at the path inselfto the given size. -
unlink(aliased asdelete): Removes the entry at the path inself. -
write: Opens the file at the entry at the path inself, writes given data to it, and closes the file.
Times
-
atime: Returns the access time of the entry at the path inself. -
birthtime: Returns the birth time of the entry at the path inself. -
ctime: On Windows, returns the birthtime. On other systems, returns a newTimeobject containing the time of the most recent metadata change to the entry at the path inself. -
lutime: LikePathname#utime, but does not follow symbolic links, and therefore changes the times of the entry at the path inself. -
mtime: Returns aTimeobject containing the time of the most recent modification to the entry at the path inself. -
utime: For the entry at the path inself, updates its access time and its modification time to the given times.
Iterating
-
each_child: Calls the block with a new pathname for each child of the entry at the path inself. -
each_entry: Calls the block with a new pathname for each entry in the entry at the path inself. -
each_line: Calls the block with each line from the file at the path inself.
Other
-
freeze: Freezesself, preventing further modifications.
Constants
- VERSION
-
The version string.
Public Class Methods
Source
# File pathname_builtin.rb, line 2751 def Pathname.getwd() self.new(Dir.getwd) end
Returns a new Pathname object containing the path to the current working directory (equivalent to Pathname.new(Dir.getwd)):
Pathname.getwd # => #<Pathname:/home>
(String | Array[String] pattern, ?Integer flags) → Array[Pathname]
(String | Array[String] pattern, ?Integer flags) { (Pathname) → untyped } → nil
Source
# File pathname_builtin.rb, line 2720 def Pathname.glob(*args, **kwargs) # :yield: pathname if block_given? Dir.glob(*args, **kwargs) {|f| yield self.new(f) } else Dir.glob(*args, **kwargs).map {|f| self.new(f) } end end
Selects filesystem entries based on the given keyword arguments base, flags, and sort; see Filename Globbing.
With no block given, returns an array of pathnames, each based on a selected filesystem entry.
With a block given, calls the block with pathnames, each based on a selected filesystem entry.
# File lib/pathname.rb, line 145 def self.mktmpdir require 'tmpdir' unless defined?(Dir.mktmpdir) if block_given? Dir.mktmpdir do |dir| dir = self.new(dir) yield dir end else self.new(Dir.mktmpdir) end end
Creates:
-
A temporary directory via
Dir.mktmpdir. -
A Pathname object that contains the path to that directory.
With no block given, returns the created pathname; the caller should delete the created directory when it is no longer needed (FileUtils.rm_r is a convenient method for the deletion):
pathname = Pathname.mktmpdir dirpath = pathname.to_s Dir.exist?(dirpath) # => true # Do something with the directory. require 'fileutils' FileUtils.rm_r(dirpath)
With a block given, calls the block with the created pathname; on block exit, automatically deletes the created directory and all its contents; returns the block’s exit value:
pathname = Pathname.mktmpdir do |p| # Do something with the directory. p end Dir.exist?(pathname.to_s) # => false
(string | Pathname) → void
Source
# File pathname_builtin.rb, line 385 def initialize(path) @path = File.path(path).dup rescue TypeError => e raise e.class, "Pathname.new requires a String, #to_path or #to_str", cause: nil end
Returns a new Pathname object based on the given path, via File.path(path).dup. the path may be a String, a File, a Dir, or another Pathname; see File.path:
Pathname.new('.') # => #<Pathname:.> Pathname.new('/usr/bin') # => #<Pathname:/usr/bin> Pathname.new(File.new('LEGAL')) # => #<Pathname:LEGAL> Pathname.new(Dir.new('.')) # => #<Pathname:.> Pathname.new(Pathname.new('.')) # => #<Pathname:.>
Public Instance Methods
# File pathname_builtin.rb, line 909 def +(other) other = Pathname.new(other) unless Pathname === other Pathname.new(plus(@path, other.path)) end
Returns a new Pathname object based on the content of self and other; argument other may be a String, a File, a Dir, or another Pathname:
pn = Pathname('foo') # => #<Pathname:foo> pn + 'bar' # => #<Pathname:foo/bar> pn + File.new('LEGAL') # => #<Pathname:foo/LEGAL> pn + Dir.new('lib') # => #<Pathname:foo/lib> pn + Pathname('bar') # => #<Pathname:foo/bar>
When other specifies a relative path (see relative?), it is combined with self to form a new pathname:
Pathname('/a/b') + 'c' # => #<Pathname:/a/b/c>
Extra component separators ('/') are removed:
Pathname('/a/b/') + 'c' # => #<Pathname:/a/b/c>
Extra current-directory components ('.') are removed:
Pathname('a') + '.' # => #<Pathname:a> Pathname('.') + 'a' # => #<Pathname:a> Pathname('.') + '.' # => #<Pathname:.>
Parent-directory components ('..') are:
-
Resolved, when possible:
Pathname('a') + '..' # => #<Pathname:.> Pathname('a/b') + '..' # => #<Pathname:a> Pathname('/') + '../a' # => #<Pathname:/a> Pathname('a') + '../b' # => #<Pathname:b> Pathname('a/b') + '../c' # => #<Pathname:a/c> Pathname('a//b/c') + '../d//e' # => #<Pathname:a//b/d//e>
-
Removed, when not needed:
Pathname('/') + '..' # => #<Pathname:/>
-
Retained, when needed:
Pathname('..') + '..' # => #<Pathname:../..> Pathname('..') + '../a' # => #<Pathname:../../a>
When other specifies an absolute path (see absolute?), equivalent to Pathname(other.to_s):
Pathname('/a') + '/b/c' # => #<Pathname:/b/c>
Occurrences of '/', '.', and '..' are preserved:
Pathname('/a') + '//b//c/./../d' # => #<Pathname://b//c/./../d>
This method does not access the file system, so other need not represent an existing (or even a valid) file or directory path:
Pathname('/var') + 'nosuch:ever' # => #<Pathname:/var/nosuch:ever>
static VALUE
path_cmp(VALUE self, VALUE other)
{
VALUE s1, s2;
char *p1, *p2;
char *e1, *e2;
if (!rb_obj_is_kind_of(other, rb_cPathname))
return Qnil;
s1 = get_strpath(self);
s2 = get_strpath(other);
p1 = RSTRING_PTR(s1);
p2 = RSTRING_PTR(s2);
e1 = p1 + RSTRING_LEN(s1);
e2 = p2 + RSTRING_LEN(s2);
while (p1 < e1 && p2 < e2) {
int c1, c2;
c1 = (unsigned char)*p1++;
c2 = (unsigned char)*p2++;
if (c1 == '/') c1 = '\0';
if (c2 == '/') c2 = '\0';
if (c1 != c2) {
if (c1 < c2)
return INT2FIX(-1);
else
return INT2FIX(1);
}
}
if (p1 < e1)
return INT2FIX(1);
if (p2 < e2)
return INT2FIX(-1);
return INT2FIX(0);
}
Compares the contents of self and other as strings; see String#<=>.
Returns:
-
-1ifself‘s string is smaller thanother’s string. -
0if the two are equal. -
1ifself‘s string is larger thanother’s string. -
nilifotheris not a Pathname.
Examples:
Pathname('a') <=> Pathname('b') # => -1 Pathname('a') <=> Pathname('ab') # => -1 Pathname('a') <=> Pathname('a') # => 0 Pathname('b') <=> Pathname('a') # => 1 Pathname('ab') <=> Pathname('a') # => 1 Pathname('ab') <=> 'a' # => nil
Two pathnames that are different may refer to the same entry in the filesystem:
Pathname('lib') <=> Pathname('./lib') # => 1
(untyped) → bool
Source
# File pathname_builtin.rb, line 415 def ==(other) return false unless Pathname === other other.path == @path end
Returns whether the stored paths in self and other are equal:
pn = Pathname('lib') pn == Pathname('lib') # => true pn == Pathname('./lib') # => false
Returns false if other is not a pathname:
pn == 'lib' # => false
() → bool
Source
static VALUE
path_absolute_p(VALUE self)
{
VALUE path = get_strpath(self);
const char *ptr = RSTRING_PTR(path);
long len = RSTRING_LEN(path);
if (len < 1) return Qfalse;
if (drive_letter) {
if (len >= 2 && ISALPHA(ptr[0]) && (ptr[1] == ':')) return Qtrue;
}
return RBOOL(isdirsep(ptr[0]));
}
Returns whether self contains an absolute path:
Pathname('/home').absolute? # => true Pathname('lib').absolute? # => false
The result is OS-dependent for some paths:
Pathname('C:/').absolute? # => true # On Windows. Pathname('C:/').absolute? # => false # Elsewhere.
() { (Pathname) → untyped } → nil
() → Enumerator[Pathname, nil]
Source
# File pathname_builtin.rb, line 837 def ascend return to_enum(__method__) unless block_given? path = @path yield self while r = chop_basename(path) path, = r break if path.empty? yield self.class.new(del_trailing_separator(path)) end end
With a block given, yields self, then a new pathname for each successive dirname in the stored path; see File.dirname:
Pathname('/path/to/some/file.rb').ascend {|dirname| p dirname} #<Pathname:/path/to/some/file.rb> #<Pathname:/path/to/some> #<Pathname:/path/to> #<Pathname:/path> #<Pathname:/>
With no block given, returns a new Enumerator.
() → Time
Source
# File pathname_builtin.rb, line 1391 def atime() File.atime(@path) end
Returns a Time object containing the access time of the entry represented by self, as reported by the filesystem; see File System Access Time:
# Pathname for a (non-existent) directory. dir_pn = Pathname('doc/foo') # => #<Pathname:doc/foo> # Create directory; establishes atime for directory. dir_pn.mkdir dir_pn.atime # => 2026-06-17 10:10:20.801115774 -0500 # Pathname for a (non-existent) file in the directory. file_pn = dir_pn.join('t.tmp') # => #<Pathname:doc/foo/t.tmp> # Create file; establishes atime for file, updates atime for directory. file_pn.write('foo') file_pn.atime # => 2026-06-17 10:11:40.987171568 -0500 dir_pn.atime # => 2026-06-17 10:11:40.96617277 -0500 # Write file; updates atime for file,but not directory. file_pn.write('bar') file_pn.atime # => 2026-06-17 10:13:22.062904563 -0500 dir_pn.atime # => 2026-06-17 10:11:40.96617277 -0500 # Read file; may update atime for file, but not directory. file_pn.read file_pn.atime # => 2026-06-17 10:13:22.062904563 -0500 dir_pn.atime # => 2026-06-17 10:11:40.96617277 -0500 # Clean up. file_pn.delete dir_pn.rmdir
# File pathname_builtin.rb, line 2030 def basename(...) self.class.new(File.basename(@path, ...)) end
Returns a new Pathname object containing all or part of the last entry of the path represented by self. Entries are delimited by the value of constant File::SEPARATOR and, if non-nil, the value of constant File::ALT_SEPARATOR.
When suffix is the empty string '', returns all of the last entry:
Pathname.new('foo/bar/baz/bat.txt').basename # => #<Pathname:bat.txt> Pathname.new('foo/bar/baz').basename # => #<Pathname:baz> File::SEPARATOR # => "/" Pathname.new('foo/bar.txt////').basename # => #<Pathname:bar.txt> File::ALT_SEPARATOR # => "\\" # On Windows. Pathname.new('foo/bar.txt//\\\\//').basename # => #<Pathname:bar.txt>
When suffix is '.*', the last filename extension, if any, is removed:
Pathname.new('foo/bar.txt').basename('.*') # => #<Pathname:bar> Pathname.new('foo/bar.txt.old').basename('.*') # => #<Pathname:bar.txt> Pathname.new('foo/bar').basename('.*') # => #<Pathname:bar>
When suffix is any string other than '' or '.*', the matching trailing substring, if any, is removed:
Pathname.new('foo/bar.txt').basename('.txt') # => #<Pathname:bar> Pathname.new('foo/bar.txt').basename('txt') # => #<Pathname:bar.> Pathname.new('foo/bar.txt').basename('*') # => #<Pathname:bar.txt> Pathname.new('foo/bar.txt').basename('.') # => #<Pathname:bar.txt>
# File pathname_builtin.rb, line 1229 def binread(...) File.binread(@path, ...) end
Behaves like read, except that the file is opened in binary mode with ASCII-8BIT encoding.
(String, ?Integer offset, ?mode: Integer | String, ?flags: Integer, ?external_encoding: encoding, ?internal_encoding: encoding, ?encoding: encoding, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish, ?invalid: :replace ?, ?undef: :replace ?, ?replace: String, ?fallback: Hash[String, String] | Proc | Method, ?xml: :text | :attr, ?universal_newline: true, ?cr_newline: true, ?crlf_newline: true) → Integer
Source
# File pathname_builtin.rb, line 1355 def binwrite(...) File.binwrite(@path, ...) end
Behaves like write, except that the file is opened in binary mode with ASCII-8BIT encoding.
() → Time
Source
# File pathname_builtin.rb, line 1424 def birthtime() File.birthtime(@path) end
Returns a new Time object containing the create time of the entry represented by self; see File System Timestamps:
# A directory and its Pathname. dir_path = 'doc/foo' dir_pn = Pathname(dir_path) # Create directory; directory birthtime established. dir_pn.mkdir dir_pn.birthtime # => 2026-06-16 17:06:10.779192552 -0500 # A file therein and its Pathname. file_path = dir_pn.join('t.tmp') file_pn = Pathname(file_path) # Create file; file birthtime established; directory birthtime not updated. file_pn.write('foo') dir_pn.birthtime # => 2026-06-16 17:06:10.779192552 -0500 file_pn.birthtime # => 2026-06-16 17:07:59.339330622 -0500 # Modify file; neither birthtime updated. file_pn.write('bar') dir_pn.birthtime # => 2026-06-16 17:06:10.779192552 -0500 file_pn.birthtime # => 2026-06-16 17:07:59.339330622 -0500 # Clean up. dir_pn.rmtree
() → bool
Source
# File pathname_builtin.rb, line 2223 def blockdev?() FileTest.blockdev?(@path) end
Returns whether self represents a path to a block device (i.e., a direct-access device):
Pathname('/dev/nvme0n1').blockdev? # => true Pathname('/dev/loop0').blockdev? # => true Pathname('/dev/tty').blockdev? # => false Pathname('/dev/null').blockdev? # => false Pathname('nosuch').blockdev? # => false Pathname($stdin).blockdev? # => false
The returned value is OS-dependent; on Windows, almost always false.
() → bool
Source
# File pathname_builtin.rb, line 2243 def chardev?() FileTest.chardev?(@path) end
Returns whether self represents a path to a character device (i.e., a sequential-access device):
Pathname('/dev/tty').chardev? # => true Pathname('/dev/null').chardev? # => true Pathname('/dev/nvme0n1').chardev? # => false Pathname('/dev/loop0').chardev? # => false Pathname($stdin).chardev? # => false Pathname('nosuch').chardev? # => false
The returned value is OS-dependent; on Windows, almost always false.
# File pathname_builtin.rb, line 1009 def children(with_directory=true) with_directory = false if @path == '.' result = Dir.children(@path) if with_directory result.map! {|e| self.class.new(File.join(@path, e))} else result.map! {|e| self.class.new(e)} end result end
Returns an array of pathnames; each represents a child of the entry represented by self, which must be an existing directory in the underlying file system.
With with_dirnames given as true (the default), each pathname contains the full entry:
Pathname('lib').children.size # => 72 Pathname('lib').children.take(3) # => [#<Pathname:lib/bundled_gems.rb>, #<Pathname:lib/bundler>, #<Pathname:lib/bundler.rb>]
With with_dirnames given as false, each pathname contains only the basename of the entry:
Pathname('lib').children(false).take(3) # => [#<Pathname:bundled_gems.rb>, #<Pathname:bundler>, #<Pathname:bundler.rb>]
Note that entries . and .. in directory are not actually children, and so are never included in the result.
# File pathname_builtin.rb, line 1532 def chmod(mode) File.chmod(mode, @path) end
Changes the mode (i.e., permissions) of the entry represented by self; see File Permissions:
# Pathname for a (non-existent) directory. dir_pn = Pathname('doc/foo') # => #<Pathname:doc/foo> # Create the directory and fetch its mode. dir_pn.mkdir dir_pn.stat.mode.to_s(8) # => "40775" # Change the directory mode and fetch the new mode. dir_pn.chmod(0777) dir_pn.stat.mode.to_s(8) # => "40777" # Pathname for a (non-existent) file in the directory. file_pn = dir_pn.join('t.tmp') # => #<Pathname:doc/foo/t.tmp> # Create the file and fetch its mode. file_pn.write('foo') file_pn.stat.mode.to_s(8) # => "100664" # Change the file mode and fetch its new mode. file_pn.chmod(0777) file_pn.stat.mode.to_s(8) # => "100777" # Clean up. file_pn.delete dir_pn.rmdir
# File pathname_builtin.rb, line 1609 def chown(owner, group) File.chown(owner, group, @path) end
Changes the owner and group of an entry (directory or file):
# Super user; all privileges. Process.uid # => 0 Process.gid # => 0 # Pathname for a (non-existent) directory. dir_pn = Pathname('doc/foo') # => #<Pathname:doc/foo> # Create the directory; fetch original owner and group. dir_pn.mkdir dir_stat = dir_pn.stat dir_stat.uid # => 0 dir_stat.gid # => 0 # Change owner; fetch current owner and group. dir_pn.chown(1000, 1000) dir_stat = dir_pn.stat dir_stat.uid # => 1000 dir_stat.gid # => 1000 Pathname for a (non-existent) file in the directory. file_pn = dir_pn.join('t.tmp') # => #<Pathname:doc/foo/t.tmp> # Create the directory; fetch original owner and group. file_pn.write('foo') file_stat = file_pn.stat file_stat.uid # => 0 file_stat.gid # => 0 # Change owner; fetch current owner and group. file_pn.chown(1000, 1000) file_stat = file_pn.stat file_stat.uid # => 1000 file_stat.gid # => 1000 # Clean up. file_pn.delete dir_pn.rmdir
Notes:
-
On Windows, the owner and group are not changed.
-
Only a process with superuser privileges can change the owner of an entry.
-
The owner of an entry can change its group to any group to which the owner belongs.
-
A +nil+ or +-1+ owner or group id is ignored.
-
The method follows symbolic links to the target entry.
(?boolish consider_symlink) → Pathname
Source
# File pathname_builtin.rb, line 629 def cleanpath(consider_symlink=false) if consider_symlink cleanpath_conservative else cleanpath_aggressive end end
Returns a new Pathname object, “cleaned” of unnecessary separators, single-dot entries, and double-dot entries.
When self is empty, returns a pathname with a single-dot entry:
Pathname('').cleanpath # => #<Pathname:.>
Separators
A lone separator is preserved:
Pathname('/').cleanpath # => #<Pathname:/>
Multiple trailing separators are removed:
Pathname('foo/////').cleanpath # => #<Pathname:foo> Pathname('foo/').cleanpath # => #<Pathname:foo>
Multiple embedded separators are reduced to a single separator:
Pathname('foo///bar').cleanpath # => #<Pathname:foo/bar>
Multiple leading separators are reduced:
# On Windows, where File.dirname('//') == '//'. Pathname('/////foo').cleanpath # => #<Pathname://foo> Pathname('/////').cleanpath # => #<Pathname://> # Otherwise, where File.dirname('//') == '/'. Pathname('/////foo').cleanpath # => #<Pathname:/foo> Pathname('/////').cleanpath # => #<Pathname:/>
Single-Dot Entries
A lone single-dot entry is preserved:
Pathname('.').cleanpath # => #<Pathname:.>
A non-lone single-dot entry, regardless of its location, is removed:
Pathname('foo/././././bar').cleanpath # => #<Pathname:foo/bar> Pathname('./foo/./././bar').cleanpath # => #<Pathname:foo/bar> Pathname('foo/./././bar/./').cleanpath # => #<Pathname:foo/bar>
Double-Dot Entries
A lone double-dot entry is preserved:
Pathname('..').cleanpath # => #<Pathname:..>
When a non-lone double-dot entry is preceded by a named entry, both are removed:
Pathname('foo/..').cleanpath # => #<Pathname:.> Pathname('foo/../bar').cleanpath # => #<Pathname:bar> Pathname('foo/../bar/..').cleanpath # => #<Pathname:.> Pathname('foo/bar/./../..').cleanpath # => #<Pathname:.>
When a non-lone double-dot entry is not preceded by a named entry, it is preserved:
Pathname('../..').cleanpath # => #<Pathname:../..>
A non-lone meaningless double-dot entry is removed:
Pathname('/..').cleanpath # => #<Pathname:/> Pathname('/../..').cleanpath # => #<Pathname:/>
Symbolic Links
If the path may contain symbolic links, consider give optional argument symlinks as true; the method then uses a more conservative algorithm that avoids breaking symbolic links. This may preserve more double-dot entries than are absolutely necessary, but without accessing the filesystem, this can’t be avoided.
Examples:
Pathname('a/').cleanpath # => #<Pathname:a> Pathname('a/').cleanpath(true) # => #<Pathname:a/> Pathname('a/.').cleanpath # => #<Pathname:a> Pathname('a/.').cleanpath(true) # => #<Pathname:a/.> Pathname('a/./').cleanpath # => #<Pathname:a> Pathname('a/./').cleanpath(true) # => #<Pathname:a/.> Pathname('a/b/.').cleanpath # => #<Pathname:a/b> Pathname('a/b/.').cleanpath(true) # => #<Pathname:a/b/.> Pathname('a/../.').cleanpath # => #<Pathname:.> Pathname('a/../.').cleanpath(true) # => #<Pathname:a/..> Pathname('a/b/../../../../c/../d').cleanpath # => #<Pathname:../../d> Pathname('a/b/../../../../c/../d').cleanpath(true) # => #<Pathname:a/b/../../../../c/../d>
() → Time
Source
# File pathname_builtin.rb, line 1464 def ctime() File.ctime(@path) end
On Windows, returns the birthtime.
On other systems, returns a new Time object containing the time of the most recent metadata change to the entry represented by self; see File System Timestamps:
# A directory and its Pathname. dir_path = 'doc/foo' dir_pn = Pathname(dir_path) # Create directory; directory ctime established. dir_pn.mkdir dir_pn.ctime # => 2026-06-16 16:44:15.86720572 -0500 # A file therein and its Pathname. file_path = dir_pn.join('t.tmp') file_pn = Pathname(file_path) # Create file; file ctime established; directory ctime updated. file_pn.write('foo') file_pn.ctime # => 2026-06-16 16:46:00.734974872 -0500 dir_pn.ctime # => 2026-06-16 16:46:00.734974872 -0500 # Write file; file ctime updated; directory ctime not updated. file_pn.write('bar') file_pn.ctime # => 2026-06-16 16:49:11.421204188 -0500 dir_pn.ctime # => 2026-06-16 16:46:00.734974872 -0500 # Read file; neither ctime updated. file_pn.read file_pn.ctime # => 2026-06-16 16:49:11.421204188 -0500 dir_pn.ctime # => 2026-06-16 16:46:00.734974872 -0500 # Clean up. dir_pn.rmtree
() { (Pathname) → untyped } → nil
() → Enumerator[Pathname, nil]
Source
# File pathname_builtin.rb, line 813 def descend return to_enum(__method__) unless block_given? vs = [] ascend {|v| vs << v } vs.reverse_each {|v| yield v } nil end
With a block given, yields a new pathname for each successive dirname in the stored path; see File.dirname:
# Absolute path. Pathname('/path/to/some/file.rb').descend {|pn| p pn } # #<Pathname:/> # #<Pathname:/path> # #<Pathname:/path/to> # #<Pathname:/path/to/some> # #<Pathname:/path/to/some/file.rb> # Relative path. Pathname('path/to/some/file.rb').descend {|pn| p pn } # #<Pathname:path> # #<Pathname:path/to> # #<Pathname:path/to/some> # #<Pathname:path/to/some/file.rb>
With no block given, returns a new Enumerator.
() → bool
Source
# File pathname_builtin.rb, line 2355 def directory?() FileTest.directory?(@path) end
Returns whether the entry represented by self is a directory:
Pathname('/etc').directory? # => true Pathname('lib').directory? # => true Pathname('README.md').directory? # => false Pathname('nosuch').directory? # => false
Source
# File pathname_builtin.rb, line 2047 def dirname() self.class.new(File.dirname(@path)) end
Returns a new pathname whose path is all but the last component of the path in self:
Pathname('/usr/lib/linux').basename # => #<Pathname:linux> Pathname('/usr').basename # => #<Pathname:usr> Pathname('/').basename # => #<Pathname:/> Pathname('lib/').basename # => #<Pathname:lib> Pathname('nosuch').basename # => #<Pathname:nosuch>
(?boolish with_directory) { (Pathname) → void } → Array[Pathname]
(?boolish with_directory) → Enumerator[Pathname, Array[Pathname]]
Source
# File pathname_builtin.rb, line 1053 def each_child(with_directory=true, &b) children(with_directory).each(&b) end
With a block given and with_dirnames given as true (the default), yields a new pathname for each child of the entry represented by self; returns an array of those pathnames:
Pathname('include').each_child {|child| p child } # #<Pathname:include/ruby> # #<Pathname:include/ruby.h> # => [#<Pathname:include/ruby>, #<Pathname:include/ruby.h>]
With a block given and with_dirnames given as false, yields a new pathname for each child of the entry represented by self with its dirname omitted; returns an array of those pathnames:
Pathname('include').each_child(false) {|child| p child } # #<Pathname:ruby> # #<Pathname:ruby.h> # => [#<Pathname:ruby>, #<Pathname:ruby.h>]
Note that entries '.' and '..' are not children.
With no block given, returns a new Enumerator.
Source
# File pathname_builtin.rb, line 2797 def each_entry(&block) # :yield: pathname return to_enum(__method__) unless block_given? Dir.foreach(@path) {|f| yield self.class.new(f) } end
With a block given, yields a new pathname for each entry in the entry represented by self; returns nil:
Pathname('include').each_entry {|entry| p entry } # #<Pathname:ruby> # #<Pathname:..> # #<Pathname:ruby.h> # #<Pathname:.> # => nil
With no block given, returns a new Enumerator.
() { (String) → untyped } → nil
() → Enumerator[String, nil]
Source
# File pathname_builtin.rb, line 780 def each_filename # :yield: filename return to_enum(__method__) unless block_given? _, names = split_names(@path) names.each {|filename| yield filename } nil end
With a block given, yields each component of the string path:
Pathname('/foo/bar/baz').each_filename {|filename| p filename } => nil
Output:
"foo" "bar" "baz"
With no block given, returns a new Enumerator.
(?String sep, ?Integer limit, ?mode: Integer | String, ?flags: Integer, ?external_encoding: encoding, ?internal_encoding: encoding, ?encoding: encoding, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish, ?chomp: boolish) { (String) → untyped } → nil
(Integer limit, ?mode: Integer | String, ?flags: Integer, ?external_encoding: encoding, ?internal_encoding: encoding, ?encoding: encoding, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish, ?chomp: boolish) { (String) → untyped } → nil
(?String sep, ?Integer limit, ?mode: Integer | String, ?flags: Integer, ?external_encoding: encoding, ?internal_encoding: encoding, ?encoding: encoding, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish, ?chomp: boolish) → Enumerator[String, nil]
(Integer limit, ?mode: Integer | String, ?flags: Integer, ?external_encoding: encoding, ?internal_encoding: encoding, ?encoding: encoding, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish, ?chomp: boolish) → Enumerator[String, nil]
Source
# File pathname_builtin.rb, line 1160 def each_line(...) # :yield: line File.foreach(@path, ...) end
With a block given, calls the block with each line from the file represented by self; returns nil:
lines = [] Pathname('COPYING').each_line {|line| lines << line } lines.take(3) # => # ["{日本語}[rdoc-ref:COPYING.ja]\n", # "\n", # "Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.\n"]
The lines are read using IO.foreach, all arguments and options are passed to that method; see details at IO.foreach.
With no block given, returns a new Enumerator.
() → bool
Source
# File pathname_builtin.rb, line 2270 def empty? if FileTest.directory?(@path) Dir.empty?(@path) else File.empty?(@path) end end
Returns whether the entry represented by self exists and is empty:
dir_pn = Pathname('example_dir') dir_pn.empty? # => false # Dir does not exist. dir_pn.mkdir dir_pn.empty? # => true # Dir exists and is empty. file_pn = Pathname('example_dir/example.txt') file_pn.empty? # => false # File does not exist. file_pn.write('') file_pn.empty? # => true # File exists and is empty. dir_pn.empty? # => false # Dir exists and is not empty. file_pn.write('foo') file_pn.empty? # => false # File exists and is not empty. file_pn.delete dir_pn.delete
Source
# File pathname_builtin.rb, line 2774 def entries() Dir.entries(@path).map {|f| self.class.new(f) } end
Returns an array of pathnames, one for each entry in the directory represented by self:
Pathname('.').entries.take(5) # => # [#<Pathname:.>, # #<Pathname:..>, # #<Pathname:gc.rb>, # #<Pathname:yjit.rb>, # #<Pathname:iseq.h>]
() → bool
Source
# File pathname_builtin.rb, line 2291 def executable?() FileTest.executable?(@path) end
Returns whether the entry represented by self is executable; calls FileTest.executable? with argument self.to_s:
Pathname('bin/gem').executable? # => true Pathname('README.md').executable? # => false
() → bool
Source
# File pathname_builtin.rb, line 2310 def executable_real?() FileTest.executable_real?(@path) end
Returns whether the entry represented by self is executable by the real user and group id of the current process; calls FileTest.executable_real? with argument self.to_s:
pn = Pathname('example') pn.write('') pn.executable_real? # => false pn.chmod(0100) pn.executable_real? # => true
() → bool
Source
# File pathname_builtin.rb, line 2325 def exist?() FileTest.exist?(@path) end
Returns whether the entry represented by self exists:
Pathname('.').exist? # => true Pathname('README.md').exist? # => true Pathname('nosuch').exist? # => false
Source
# File pathname_builtin.rb, line 2124 def expand_path(...) self.class.new(File.expand_path(@path, ...)) end
Returns a new pathname containing the absolute path for self.
Evaluates a relative path with respect to the directory given by dirpath:
Dir.chdir('/snap') # Default dirpath. Pathname('README').expand_path # => #<Pathname:/snap/README> Pathname('bin').expand_path # => #<Pathname:/snap/bin> Pathname('bin/../var').expand_path # => #<Pathname:/snap/var> # Cleaned. # Other dirpath. Pathname('../zip').expand_path('/usr/bin/ruby') # => #<Pathname:/usr/bin/zip> Dir.chdir('/usr/bin') Pathname('../../snap').expand_path(__FILE__) # => #<Pathname:/usr/snap>
Evaluates an absolute path without respect to dirpath:
Pathname('/snap').expand_path # => #<Pathname:/snap> Pathname('/snap').expand_path.expand_path('nosuch') # => #<Pathname:/snap> Pathname('/snap/../snap').expand_path # => #<Pathname:/snap> # Cleaned.
More examples:
Dir.chdir('/usr/bin') Pathname('../../snap').expand_path(__FILE__) # => #<Pathname:/usr/snap> Pathname('../../snap').expand_path # => #<Pathname:/snap>
Source
# File pathname_builtin.rb, line 2085 def extname() File.extname(@path) end
Returns the filename extension of self – usually the portion of the string path beginning from the last period:
Pathname('t.rb').extname # => ".rb" Pathname('foo.bar.t.rb').extname # => ".rb" Pathname('foo/bar/t.rb').extname # => ".rb" Pathname('nosuch.txt').extname # => ".txt" # Path need not exist.
Returns the entire string when there is no period:
Pathname('foo').extname # => ""
Returns an empty string when the only period is the first character:
Pathname('.irbrc').extname # => ""
Returns an empty string or '.' when path ends with a period:
Pathname('foo.').extname # => "" # On Windows. Pathname('foo.').extname # => "." # Elsewhere. Pathname('foo....').extname # => "" # On Windows. Pathname('foo....').extname # => "." # Elsewhere.
() → bool
Source
# File pathname_builtin.rb, line 2370 def file?() FileTest.file?(@path) end
Returns whether the entry at the path in self exists and is a regular file:
Pathname('README.md').file? # => true Pathname('lib/').file? # => false Pathname('nosuch').file? # => false
# File lib/pathname.rb, line 74 def find(ignore_error: true) # :yield: pathname return to_enum(__method__, ignore_error: ignore_error) unless block_given? require 'find' if @path == '.' Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f.delete_prefix('./')) } else Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f) } end end
With a block given, performs a depth-first traversal of the path in self; calls the block with each found path:
paths = [] Pathname('lib').find {|path| paths << path } paths.size # => 909 paths.take(3) # => # [#<Pathname:lib>, # #<Pathname:lib/English.gemspec>, # #<Pathname:lib/English.rb>]
When self contains '.', the found paths omit the leading './':
paths = [] Dir.chdir('lib') do Pathname('.').find {|path| paths << path } end paths.take(3) # # => # [#<Pathname:.>, # #<Pathname:English.gemspec>, # #<Pathname:English.rb>]
This method calls method Find.find; therefore method Find.prune may be used in the block:
files = [] Pathname('.').find do |path| Find.prune if File.basename(path) == 'test' next unless File.file?(path) && File.extname(path) == '.rb' files << path end files.size # => 6690 files.take(3) # # => # [#<Pathname:KNOWNBUGS.rb>, # #<Pathname:array.rb>, # #<Pathname:ast.rb>]
Raises an exception if the path in self cannot be read.
When keyword argument ignore_error is given as true (the default), certain exceptions during traversal are ignored (i.e., silently rescued): Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL; when given as false, no exceptions are rescued.
Note that these exceptions may be ignored only in Pathname#find traversal code; an exception raised before traversal begins, or raised while in the block is not ignored. Each of the calls below raises an Errno::ENOENT exception that is not ignored:
Pathname('nosuch').find { } Pathname('lib').find {|entry| raise Errno::ENOENT }
With no block given, returns a new Enumerator.
# File pathname_builtin.rb, line 1664 def fnmatch(pattern, ...) File.fnmatch(pattern, @path, ...) end
Returns whether string pattern matches against the string path in self, under the control of the given flags; see Filename Matching.
# File pathname_builtin.rb, line 1672 def fnmatch?(pattern, ...) File.fnmatch?(pattern, @path, ...) end
Same as fnmatch.
() → Pathname
Source
# File pathname_builtin.rb, line 396 def freeze super @path.freeze self end
Freezes self, preventing further modifications; see Frozen Objects.
Object#freeze
() → String
Source
# File pathname_builtin.rb, line 1695 def ftype() File.ftype(@path) end
Returns the string type of the object at the path in self:
Pathname('README.md').ftype # => "file" Pathname('lib').ftype # => "directory" Pathname('/dev/null').ftype # => "characterSpecial" Pathname('/dev/loop0').ftype # => "blockSpecial" File.mkfifo('/tmp/pipe', 0666) Pathname('/tmp/pipe').ftype # => "fifo" File.symlink('lib', 'lib_link') Pathname('lib_link').ftype # => "link" require 'socket' UNIXServer.new('/tmp/socket') Pathname('/tmp/socket').ftype # => "socket"
Returns 'unknown' if the type cannot be determined.
(String | Array[String] pattern, ?Integer flags) → Array[Pathname]
(String | Array[String] pattern, ?Integer flags) { (Pathname) → untyped } → nil
Source
# File pathname_builtin.rb, line 2735 def glob(*args, **kwargs) # :yield: pathname if block_given? Dir.glob(*args, **kwargs, base: @path) {|f| yield self + f } else Dir.glob(*args, **kwargs, base: @path).map {|f| self + f } end end
() → bool
Source
# File pathname_builtin.rb, line 2339 def grpowned?() FileTest.grpowned?(@path) end
Returns whether the filesystem entry for the path stored in self exists, and the effective group id of the calling process is the owner of the entry:
Pathname('README.md').grpowned? # => true Pathname('lib').grpowned? # => true Pathname('/etc/passwd').grpowned? # => false Pathname('nosuch').grpowned? # => false
Returns false on Windows.
# File pathname_builtin.rb, line 969 def join(*args) return self if args.empty? result = args.pop result = Pathname.new(result) unless Pathname === result return result if result.absolute? args.reverse_each {|arg| arg = Pathname.new(arg) unless Pathname === arg result = arg + result return result if result.absolute? } self + result end
Joins the string-converted given objects to the string path in self; returns a new pathname containing the joined string:
Pathname('foo').join # => #<Pathname:foo> Pathname('foo').join('bar') # => #<Pathname:foo/bar> Pathname('foo').join('bar', 'baz') # => #<Pathname:foo/bar/baz> Pathname('foo').join(Pathname('bar')) # => #<Pathname:foo/bar>
# File pathname_builtin.rb, line 1556 def lchmod(mode) File.lchmod(mode, @path) end
Not supported on some platforms (raises Errno::ENOTSUP).
When supported: like Pathname::chmod, but does not follow symbolic links, and therefore changes the mode of the entry specified by self:
File.write('t.tmp', '') File.symlink('t.tmp', 'link') File.stat('t.tmp').mode.to_s(8) # => "100664" File.stat('link').mode.to_s(8) # => "100664" Pathname('link').lchmod(0777) File.stat('t.tmp').mode.to_s(8) # => "100664" File.stat('link').mode.to_s(8) # => "100777" File.delete('t.tmp') File.delete('link')
# File pathname_builtin.rb, line 1654 def lchown(owner, group) File.lchown(owner, group, @path) end
Not supported on some platforms (raises exception).
Calling process must have superuser privileges.
When supported: like Pathname#chown, but does not follow symbolic links, and therefore changes the ownership of the entry at the path in self:
# Super user; all privileges. Process.uid # => 0 Process.gid # => 0 # Create regular file and symbolic link to it. File.write('t.tmp', '') File.symlink('t.tmp', 'link') # Capture original statuses. fstat0 = File.stat('t.tmp') # Method ::stat; status of file. lstat0 = File.lstat('link') # Method ::lstat; status of link. # Original user ids and group ids. fstat0.uid # => 0 fstat0.gid # => 0 lstat0.uid # => 0 lstat0.gid # => 0 # Change ids for link. Pathname('link').lchown(1000, 1000) # Capture new statuses. fstat1 = File.stat('t.tmp') lstat1 = File.lstat('link') # User id and group id for file not changed. fstat1.uid # => 0 fstat1.gid # => 0 # User id and group id for link changed. p lstat1.uid # => 1000 p lstat1.gid # => 1000 # Clean up. File.delete('t.tmp') File.delete('link')
() → ::File::Stat
Source
# File pathname_builtin.rb, line 1852 def lstat() File.lstat(@path) end
Returns a File::Stat object for the path in self; does not follow symbolic links, and therefore returns the stat object for that path, regardless of whether it is a symbolic link:
File.write('t.tmp', '') sleep(1) File.symlink('t.tmp', 'link') pn = Pathname('link') # => #<Pathname:link> # Method stat: follows link to 't.tmp'. pn.stat.ctime # => 2026-06-13 15:02:46.562620885 -0500 # Method lstat; does not follow link. pn.lstat.ctime # => 2026-06-13 15:02:47.563619647 -0500 File.delete('t.tmp') File.delete('link')
# File pathname_builtin.rb, line 1994 def lutime(atime, mtime) File.lutime(atime, mtime, @path) end
Like Pathname#utime, but does not follow symbolic links, and therefore changes the times of the entry in self, regardless of whether it is a symbolic link:
# Create a file and a link to it. file_path = 't.tmp' link_path = 'link' File.write(file_path, '') File.symlink(file_path, link_path) # Take snapshots of both. file_stat = File.stat(file_path) link_stat = File.lstat(link_path) # Fetch access times and modification times of both. file_stat.atime # => 2026-06-15 11:03:29.600373255 -0500 file_stat.mtime # => 2026-06-15 11:03:22.247352211 -0500 link_stat.atime # => 2026-06-15 11:03:29.251372254 -0500 link_stat.mtime # => 2026-06-15 11:03:26.66436484 -0500 # Update access time and modification time of the link. pn = Pathname(link_path) time = Time.now # => 2026-06-15 11:08:07.384287523 -0500 pn.lutime(time, time) # Take fresh snapshots of both. file_stat = File.stat(file_path) link_stat = File.lstat(link_path) # Fetch access time and modification time of file (not changed). file_stat.atime # => 2026-06-15 11:03:29.600373255 -0500 file_stat.mtime # => 2026-06-15 11:03:22.247352211 -0500 # Fetch access time and modification time of link (changed). link_stat.atime # => 2026-06-15 11:08:29.847301399 -0500 link_stat.mtime # => 2026-06-15 11:08:07.384287523 -0500 # Clean up. File.delete(file_path) File.delete(link_path)
Arguments atime and mtime may be Time objects (as above).
Either or both may be integers; when an integer i is passed, Time.new(i) is used.
Either or both may be nil, in which case Time.now is used.
# File pathname_builtin.rb, line 1719 def make_link(old) File.link(old, @path) end
Not available on some systems.
Creates a new entry at the path in self for the existing entry at path using a hard link:
File.write('doc/t.tmp', 'foo') Pathname('lib/u.tmp').make_link('doc/t.tmp') File.read('lib/u.tmp') # => "foo" File.write('lib/u.tmp', 'bar') File.read('doc/t.tmp') # => "bar" File.delete('doc/t.tmp') File.read('lib/u.tmp') # => "bar" File.delete('lib/u.tmp')
Raises an exception if the entry at the path in self exists.
# File pathname_builtin.rb, line 1873 def make_symlink(old) File.symlink(old, @path) end
Creates a symbolic link at the path in self to the entry at path:
# Create Pathnames. file_pn = Pathname('doc/extension.rdoc') # => #<Pathname:doc/extension.rdoc> target_pn = Pathname('..').join(file_pn) # => #<Pathname:../doc/extension.rdoc> link_pn = Pathname('lib/u.tmp') # => #<Pathname:lib/u.tmp> # Create link and verify. link_pn.make_symlink(target_pn) file_pn.read == link_pn.read # => true link_pn.delete # Clean up.
# File pathname_builtin.rb, line 2821 def mkdir(...) Dir.mkdir(@path, ...) end
Creates a directory in the underlying file system at the path in self, with the given permissions; see File Permissions:
Dir.mkdir('foo') File.stat(Dir.new('foo')).mode.to_s(8) # => "40775" Dir.mkdir('bar', 0644) File.stat(Dir.new('bar')).mode.to_s(8) # => "40644" Dir.rmdir('foo') Dir.rmdir('bar')
Argument permissions is ignored on Windows.
() → self
Source
# File pathname_builtin.rb, line 466 def mkpath(mode: nil) path = @path == '/' ? @path : @path.chomp('/') stack = [] until File.directory?(path) || (parent = File.dirname(path)) == path stack.push path path = parent end stack.reverse_each do |dir| dir = dir == '/' ? dir : dir.chomp('/') if mode Dir.mkdir dir, mode File.chmod mode, dir else Dir.mkdir dir end rescue SystemCallError raise unless File.directory?(dir) end self end
Creates a directory at the path in self; creates intermediate directories as needed:
pn = Pathname('foo/bar/baz') pn.directory? # => false pn.mkpath # Creates directories 'foo', 'foo/bar', 'foo/bar/baz'. pn.directory? # => true pn.rmtree # Clean up.
When mode is given, directories are created with those permissions; see File Permissions. The permissions for already-existing directories are not changed.
() → bool
Source
# File pathname_builtin.rb, line 725 def mountpoint? begin stat1 = self.lstat stat2 = self.parent.lstat stat1.dev != stat2.dev || stat1.ino == stat2.ino rescue Errno::ENOENT false end end
Returns whether entry at the path in self is a mountpoint:
Pathname('/').mountpoint? # => true Pathname('/etc').mountpoint? # => false Pathname('nosuch').mountpoint? # => false
() → Time
Source
# File pathname_builtin.rb, line 1497 def mtime() File.mtime(@path) end
Returns a Time object containing the time of the most recent modification to the entry represented by self; see File System Timestamps:
# A directory and its Pathname. dir_path = 'doc/foo' dir_pn = Pathname(dir_path) # Create directory; directory mtime established. dir_pn.mkdir dir_pn.mtime # => 2026-06-28 16:38:02.675780521 -0500 # A file therein and its Pathname. file_path = dir_pn.join('t.tmp') file_pn = Pathname(file_path) # Create file; file mtime established; directory mtime updated. file_pn.write('foo') dir_pn.mtime # => 2026-06-28 16:41:23.107750483 -0500 file_pn.mtime # => 2026-06-28 16:41:23.107750483 -0500 # Modify file; file mtime updated; directory mtime unchanged. file_pn.write('bar') dir_pn.mtime # => 2026-06-28 16:41:23.107750483 -0500 file_pn.mtime # => 2026-06-28 16:42:48.869163049 -0500 # Clean up. dir_pn.rmtree
(?string | int mode, ?int perm, ?flags: Integer, ?external_encoding: encoding, ?internal_encoding: encoding, ?encoding: encoding, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish, ?path: path, ?invalid: :replace | nil, ?undef: :replace | nil, ?replace: String | nil, ?fallback: Hash[string, string] | ^(String) → string | Method | nil, ?xml: :text | :attr | nil, ?cr_newline: bool, ?crlf_newline: bool, ?universal_newline: bool) → File
[T] (?string | int mode, ?int perm, ?mode: Integer | String, ?flags: Integer, ?external_encoding: encoding, ?internal_encoding: encoding, ?encoding: encoding, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish, ?path: path, ?invalid: :replace | nil, ?undef: :replace | nil, ?replace: String | nil, ?fallback: Hash[string, string] | ^(String) → string | Method | nil, ?xml: :text | :attr | nil, ?cr_newline: bool, ?crlf_newline: bool, ?universal_newline: bool) { (File) → T } → T
Source
# File pathname_builtin.rb, line 1745 def open(...) # :yield: file File.open(@path, ...) end
Creates a File object for the entry at the path in self and opens the file; see File.new for details.
With a block given, calls the block with the file; closes the file on block exit:
Pathname('README.md').open {|file| file.size } # => 3469
With no block given, returns the file:
file = Pathname('README.md').open # => #<File:README.md> file.size # => 3469 file.close
Source
# File pathname_builtin.rb, line 2868 def opendir(&block) # :yield: dir Dir.open(@path, &block) end
Creates a Dir object dir for the directory at the path represented by self; opens dir.
With a block given, calls the block with dir; on block exit, closes dir and returns the block’s return value:
pn = Pathname('.') pn.opendir {|dir| dir.entries.take(3) } # => ["README.md", "html", ".git"]
With no block given, returns the open directory dir:
dir = pn.opendir # => #<Dir:.> dir.entries.take(3) # => ["README.md", "html", ".git"] dir.close
() → bool
Source
# File pathname_builtin.rb, line 2433 def owned?() FileTest.owned?(@path) end
Returns whether the entry at the path represented by self exists and is owned by the user of the current process:
pn = Pathname('doc/t.tmp') pn.write('foo') pn.owned? # => true pn.delete pn = Pathname('doc/tmp') pn.mkdir pn.owned? # => true pn.rmdir Pathname('/etc').owned? # => false
Source
# File pathname_builtin.rb, line 708 def parent self + '..' end
Returns a new pathname representing the parent directory of the entry represented by self:
pn = Pathname('/etc/passwd') # => #<Pathname:/etc/passwd> pn.parent # => #<Pathname:/etc>
() → bool
Source
# File pathname_builtin.rb, line 2388 def pipe?() FileTest.pipe?(@path) end
Returns whether entry at the path in self is a pipe:
path = '/tmp/foo' File.mkfifo(path) pn = Pathname(path) # => #<Pathname:/tmp/foo> pn.pipe? # => true Pathname('.').pipe? # => false pn.delete # Clean up.
(?Integer length, ?Integer offset, ?mode: Integer | String, ?flags: Integer, ?external_encoding: encoding, ?internal_encoding: encoding, ?encoding: encoding, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish) → String
Source
# File pathname_builtin.rb, line 1221 def read(...) File.read(@path, ...) end
Reads and returns some or all of the content of the file whose path is self.to_s.
With no arguments given, reads in text mode and returns the entire content of the file:
Pathname.new('t.txt').read # => "First line\nSecond line\n\nFourth line\nFifth line\n" Pathname.new('t.ja').read # => "こんにちは" Pathname.new('t.dat').read # => "\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94"
On Windows, text mode can terminate reading and leave bytes in the file unread when encountering certain special bytes. Consider using binread if all bytes in the file should be read.
With argument length given, returns length bytes if available:
Pathname.new('t.txt').read(7) # => "First l" Pathname.new('t.ja').read(7) # => "\xE3\x81\x93\xE3\x82\x93\xE3" Pathname.new('t.dat').read(7) # => "\xFE\xFF\x99\x90\x99\x91\x99"
Returns all bytes if length is larger than the files size:
Pathname.new('t.txt').read(700) # => "First line\r\nSecond line\r\n\r\nFourth line\r\nFifth line\r\n" Pathname.new('t.ja').read(700) # => "\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF" Pathname.new('t.dat').read(700) # => "\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94"
With arguments length and offset given, returns length bytes if available, beginning at the given offset:
Pathname.new('t.txt').read(10, 2) # => "rst line\r\n" Pathname.new('t.ja').read(10, 2) # => "\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1" Pathname.new('t.dat').read(10, 2) # => "\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94"
Returns nil if offset is past the end of the file:
Pathname.new('t.txt').read(10, 200) # => nil
Optional keyword arguments opts specify:
() → bool
Source
# File pathname_builtin.rb, line 2453 def readable?() FileTest.readable?(@path) end
Returns whether the entry at the path in self is readable by the owner and group of the current process:
pn = Pathname('/tmp/secret.txt') pn.write('foo') pn.readable? # => true pn.chmod(0o000) pn.readable? # => false pn.delete # Clean up. Pathname('nosuch').readable? # => false
() → bool
Source
# File pathname_builtin.rb, line 2486 def readable_real?() FileTest.readable_real?(@path) end
Like readable?, but checks against the real user and group ids instead of the effective ids.
(?String sep, ?Integer limit, ?mode: Integer | String, ?flags: Integer, ?external_encoding: encoding, ?internal_encoding: encoding, ?encoding: encoding, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish, ?chomp: boolish) → Array[String]
(Integer limit, ?mode: Integer | String, ?flags: Integer, ?external_encoding: encoding, ?internal_encoding: encoding, ?encoding: encoding, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish, ?chomp: boolish) → Array[String]
Source
# File pathname_builtin.rb, line 1285 def readlines(...) File.readlines(@path, ...) end
Returns an array of all lines read from the source at the path in self, which must be the path to a file.
Examples here use a file defined at IO Example Files.
With no arguments given, parses lines from the file at the given path, as determined by the default line separator, and returns those lines in an array:
pn = Pathname('t.txt') ppn.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"]
With argument sep given, parses lines as determined by that line separator (see IO Line Separator):
pn.readlines('li') # => ["First li", "ne\nSecond li", "ne\n\nFourth li", "ne\nFifth li", "ne\n"] pn.readlines('') # Special "paragraphs" separator value. # => ["First line\nSecond line\n\n", "Fourth line\nFifth line\n"] pn.readlines(nil) # Special "slurp" separator value. # => ["First line\nSecond line\n\nFourth line\nFifth line\n"]
With argument limit given, parses lines as determined by the default line separator and the given line-length limit (see IO Line Separator and IO Line Limit):
pn.readlines(7) # => ["First l", "ine\n", "Second ", "line\n", "\n", "Fourth ", "line\n", "Fifth l", "ine\n"]
With arguments sep and limit given, combines the two behaviors (see IO Line Separator and Line Limit).
Optional keyword arguments options specify:
() → untyped
Source
# File pathname_builtin.rb, line 1766 def readlink() self.class.new(File.readlink(@path)) end
Returns a new pathname containing the path to the entry represented by self:
# Create Pathnames. file_pn = Pathname('doc/extension.rdoc') # => #<Pathname:doc/extension.rdoc> target_pn = Pathname('..').join(file_pn) # => #<Pathname:../doc/extension.rdoc> link_pn = Pathname('lib/u.tmp') # => #<Pathname:lib/u.tmp> link_pn.make_symlink(target_pn) link_pn.readlink # => #<Pathname:../doc/extension.rdoc> link_pn.delete
# File pathname_builtin.rb, line 2199 def realdirpath(...) self.class.new(File.realdirpath(@path, ...)) end
Returns a new pathname containing the real (absolute) pathname of the path in self; the new path is the path in the actual filesystem, and does not contain useless dot-entries ('.' or '..') or symbolic links:
Pathname('/etc/./passwd/../../var').realdirpath # => #<Pathname:/var>
Only the last component of the new path may be nonexistent:
Pathname('/etc/./passwd/../../var/nosuch').realdirpath # => #<Pathname:/var/nosuch> Pathname('/etc/./passwd/../../var/nosuch/nosuch').realdirpath # Raises Errno::ENOENT: No such file or directory.
Method realpath is similar, but requires all components to exist.
# File pathname_builtin.rb, line 2170 def realpath(...) self.class.new(File.realpath(@path, ...)) end
Returns a new pathname containing the real (absolute) pathname of the path in self; the new path is the path in the actual filesystem, and does not contain useless dot-entries ('.' or '..') or symbolic links:
Pathname('/etc/./passwd/../../var').realpath # => #<Pathname:/var>
All components of the new path must exist:
Pathname('/etc/./passwd/../../var/nosuch').realpath # Raises Errno::ENOENT: No such file or directory.
Method realdirpath is similar, but does not require the last component to exist.
() → bool
Source
# File pathname_builtin.rb, line 754 def relative? !absolute? end
Returns whether self contains a relative path:
Pathname('lib').relative? # => true Pathname('/home').relative? # => false
The result is OS-dependent for some paths:
Pathname('C:/').relative? # => false # On Windows. Pathname('C:/').relative? # => true # Elsewhere.
# File pathname_builtin.rb, line 1093 def relative_path_from(base_directory) base_directory = Pathname.new(base_directory) unless base_directory.is_a? Pathname dest_directory = self.cleanpath.path base_directory = base_directory.cleanpath.path dest_prefix = dest_directory dest_names = [] while r = chop_basename(dest_prefix) dest_prefix, basename = r dest_names.unshift basename if basename != '.' end base_prefix = base_directory base_names = [] while r = chop_basename(base_prefix) base_prefix, basename = r base_names.unshift basename if basename != '.' end unless same_paths?(dest_prefix, base_prefix) raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}" end while !dest_names.empty? && !base_names.empty? && same_paths?(dest_names.first, base_names.first) dest_names.shift base_names.shift end if base_names.include? '..' raise ArgumentError, "base_directory has ..: #{base_directory.inspect}" end base_names.fill('..') relpath_names = base_names + dest_names if relpath_names.empty? Pathname.new('.') else Pathname.new(File.join(*relpath_names)) end end
Returns a pathname containing the relative filesystem path from the given source to the path in self; source must be a directory path or a pathname containing a directory path:
Pathname('.').relative_path_from('doc/language') # => #<Pathname:../..> Pathname('doc/language').relative_path_from('.') # => #<Pathname:doc/language> Pathname('doc').relative_path_from('doc/language') # => #<Pathname:..>
The paths need not exist:
Pathname('nosuch').relative_path_from('nosuch/foo/bar/baz') # => #<Pathname:../../..>
The two paths must be either both absolute or both relative:
Pathname('/var').relative_path_from('/etc') # => #<Pathname:../var> Pathname('/var').relative_path_from('doc') # Raises ArgumentError Pathname('doc').relative_path_from('/etc') # Raises ArgumentError
Raises an exception if there is no such relative path:
Pathname('foo').relative_path_from('..') # Raises ArgumentError
(Pathname | string new_name) → 0
Source
# File pathname_builtin.rb, line 1809 def rename(to) File.rename(@path, to) end
Renames the entry at the path in self to the entry given in new_name, which may be either a path or another pathname:
# Create source and destination pathnames and directories. pn_srcdir = Pathname('/tmp/src') # => #<Pathname:/tmp/src> pn_srcdir.mkdir pn_dstdir = Pathname('/tmp/dst') # => #<Pathname:/tmp/dst> pn_dstdir.mkdir # Create source file pathname and file. pn_srcfile = pn_srcdir.join('t.tmp') # => #<Pathname:/tmp/src/t.tmp> pn_srcfile.write('foo') # Create destination file pathname. pn_dstfile = pn_dstdir.join('u.tmp') # => #<Pathname:/tmp/dst/u.tmp> # Rename source file as destination file. pn_srcfile.rename(pn_dstfile) pn_srcfile.exist? # => false pn_dstfile.exist? # => true
Works for directories, too:
pn_dstdir.rename('/tmp/foo') pn_dstdir.exist? # => false Pathname('/tmp/foo').exist? # => true
Clean up.
pn_srcdir.rmtree Pathname('/tmp/foo').rmtree
Raises SystemCallError if the entry cannot be renamed.
() → 0
Source
# File pathname_builtin.rb, line 2840 def rmdir() Dir.rmdir(@path) end
Deletes the directory at the path in self; returns 0:
pn = Pathname('doc/foo') pn.mkdir pn.rmdir
Raises an exception if the entry at the path in self is not a directory, or if the directory is not empty.
Use method rmtree to delete the entire filetree at the path.
Source
# File lib/pathname.rb, line 106 def rmtree(noop: nil, verbose: nil, secure: nil) # The name "rmtree" is borrowed from File::Path of Perl. # File::Path provides "mkpath" and "rmtree". require 'fileutils' FileUtils.rm_rf(@path, noop: noop, verbose: verbose, secure: secure) self end
Deletes the entire filetree at the path in self; returns 0:
dir_pn = Pathname('foo/bar/baz') # => #<Pathname:foo/bar/baz> dir_pn.mkpath # Create 'baz' and intermediate directories. file_pn = dir_pn.join('t.tmp') # => #<Pathname:foo/bar/baz/t.tmp> file_pn.write('foo') # Create file at nested directory 'baz'. Pathname('foo').rmtree # Delete the entire tree at directory 'foo'. Pathname('foo').exist? # => false
Use method rmdir to delete a single (empty) directory.
() → bool
Source
static VALUE
path_root_p(VALUE self)
{
VALUE path = get_strpath(self);
if (RSTRING_LEN(path) == 0) return Qfalse;
const char *ptr = RSTRING_PTR(path), *end = RSTRING_END(path);
rb_encoding *enc = rb_enc_get(path);
const char *base = rb_enc_path_skip_prefix_root(ptr, end, enc);
return RBOOL(base == end);
}
Returns whether the path in self points to a root directory.
On a non-Windows system, a root directory path is one whose name begins with one or more slash characters (‘’/‘):
Pathname('/').root? # => true Pathname('////').root? # => true Pathname('/usr').root? # => false Pathname('foo').root? # => false
Does not resolve dot directories:
Pathname('/usr/.').root? # => false Pathname('/usr/..').root? # => false
On a Windows system, a root directory path is one whose name begins as above, or with a device letter followed by a colon character (':') and one or more slash characters (‘’/‘):
Pathname('/').root? # => true Pathname('////').root? # => true Pathname('C:/').root? # => true Pathname('C:////').root? # => true Pathname('c:/').root? # => true Pathname('H:/').root? # => true Pathname('C:/m').root? # => false Pathname('C:').root? # => false
() → bool
Source
# File pathname_builtin.rb, line 2534 def setgid?() FileTest.setgid?(@path) end
Returns whether the setgid bit is set in the permissions for the entry at the path in self:
# Create a file and get its permissions and setgid? setting. pn = Pathname('doc/t.tmp') pn.write('foo') mode = pn.stat.mode.to_s(8) # => "100664" pn.setgid? # => false # Set the bit. pn.chmod(0o2644) mode = pn.stat.mode.to_s(8) # => "102644" pn.setgid? # => true pn.delete # Clean up.
On Windows, the bit is never set; the method always returns false.
() → bool
Source
# File pathname_builtin.rb, line 2510 def setuid?() FileTest.setuid?(@path) end
Returns whether the setuid bit is set in the permissions for the entry at the path in self:
# Create a file and get its permissions and setuid? setting. pn = Pathname('doc/t.tmp') pn.write('foo') mode = pn.stat.mode.to_s(8) # => "100664" pn.setuid? # => false # Set the bit. pn.chmod(0o4644) mode = pn.stat.mode.to_s(8) # => "104644" pn.setuid? # => true pn.delete # Clean up.
On Windows, the bit is never set; the method always returns false.
() → Integer
Source
# File pathname_builtin.rb, line 2554 def size() FileTest.size(@path) end
Returns the size of the entry at the path in self:
Pathname('README.md').size # => 3469 Pathname('doc').size # => 4096 pn = Pathname('doc/t.tmp') pn.write('') pn.size # => 0 pd.delete # Clean up.
Raises an exception if the entry does not exist.
Source
# File pathname_builtin.rb, line 2579 def size?() FileTest.size?(@path) end
If the file or directory entry at the path in self exists, returns its size if non-zero, or nil if zero:
pn = Pathname('doc/t.tmp') pn.write('foo') pn.size? # => 3 pn.write('') pn.size? # => nil
Returns nil if the entry does not exist:
pn.delete pn.size? # => nil
() → untyped
Source
# File pathname_builtin.rb, line 2411 def socket?() FileTest.socket?(@path) end
Returns whether entry at the path in self is a socket:
require 'socket' path = 'doc/socket' server = UNIXServer.new(path) # => #<UNIXServer:doc/socket> pn = Pathname(path) # => #<Pathname:doc/socket> pn.socket? # => true server.close pn.unlink Pathname('README.md').socket? # => false Pathname('nosuch').socket? # => false
Returns false on Windows.
# File pathname_builtin.rb, line 2140 def split() array = File.split(@path) raise TypeError, 'wrong argument type nil (expected Array)' unless Array === array array.map {|f| self.class.new(f) } end
Returns a 2-element array containing dirname and basename:
Pathname('lib/pathname.rb').split # => [#<Pathname:lib>, #<Pathname:pathname.rb>] Pathname('README.md').split # => [#<Pathname:.>, #<Pathname:README.md>] Pathname('').split # => [#<Pathname:.>, #<Pathname:>] Pathname('nosuch/foo/bar').split # => [#<Pathname:nosuch/foo>, #<Pathname:bar>]
Source
# File pathname_builtin.rb, line 1825 def stat() File.stat(@path) end
Returns a File::Stat object for the entry at the path in self:
Pathname('README.md').stat.inspect => "#<File::Stat dev=0x10302, ino=22941341, mode=0100664, nlink=1, uid=1000, gid=1000, rdev=0x0, size=3469, blksize=4096, blocks=8, atime=2026-07-10 15:24:17.476506084 -0500, mtime=2026-07-07 10:23:27.320088262 -0500, ctime=2026-07-07 10:23:27.320088262 -0500>" Pathname('doc').stat.inspect => "#<File::Stat dev=0x10302, ino=22941930, mode=040775, nlink=22, uid=1000, gid=1000, rdev=0x0, size=4096, blksize=4096, blocks=8, atime=2026-07-11 10:05:20.480330738 -0500, mtime=2026-07-11 10:05:06.34333645 -0500, ctime=2026-07-11 10:05:06.34333645 -0500>"
() → untyped
Source
# File pathname_builtin.rb, line 2600 def sticky?() FileTest.sticky?(@path) end
Returns whether the sticky bit is set for the entry at the path in self:
pn = Pathname('t.tmp') pn.write('foo') pn.stat.mode.to_s(8) # => "100664" pn.sticky? # => false pn.chmod(0o1644) pn.stat.mode.to_s(8) # => "101644" pn.sticky? # => true pn.delete
Returns false on Windows.
(Regexp | string pattern, string | Hash[String, String] replacement) → Pathname
(Regexp | string pattern) { (String match) → string } → Pathname
Source
static VALUE
path_sub(int argc, VALUE *argv, VALUE self)
{
VALUE str = get_strpath(self);
if (rb_block_given_p()) {
str = rb_block_call(str, id_sub, argc, argv, 0, 0);
}
else {
str = rb_funcallv(str, id_sub, argc, argv);
}
return rb_class_new_instance(1, &str, rb_obj_class(self));
}
Returns a new pathname whose path is the path in self, after the specified substitutions.
Argument pattern may be a string or a Regexp; argument replacement may be a string or a hash.
Varying types for the argument values makes this method very versatile.
Below are some simple examples; for many more related examples (using strings, not pathnames), see Substitution Methods.
With arguments pattern and string replacement given, replaces the first matching substring with the given replacement string:
pn = Pathname('abracadabra.txt') # => #<Pathname:abracadabra.txt> pn.sub('bra', 'xyzzy') # => #<Pathname:axyzzycadabra.txt> pn.sub(/bra/, 'xyzzy') # => #<Pathname:axyzzycadabra.txt> pn.sub('nope', 'xyzzy') # => #<Pathname:abracadabra.txt>
With arguments pattern and hash replacement given, replaces the first matching substring with a value from the given replacement hash, or removes it:
h = {'a' => 'A', 'b' => 'B', 'c' => 'C'} pn.sub('b', h) # => #<Pathname:aBracadabra.txt> pn.sub(/b/, h) # => #<Pathname:aBracadabra.txt> pn.sub(/d/, h) # => #<Pathname:abracaabra.txt> # 'd' removed.
With argument pattern and a block given, calls the block with the first matching substring; replaces that substring with the block’s return value:
pn.sub('b') {|match| match.upcase } # => #<Pathname:aBracadabra.txt> pn.sub(/X/) {|match| match.upcase } # => #<Pathname:abracadabra.txt>
(string replacement) → Pathname
Source
static VALUE
path_sub_ext(VALUE self, VALUE repl)
{
VALUE path = get_strpath(self);
long len = RSTRING_LEN(path);
const char *ptr = RSTRING_PTR(path);
const char *ext = ruby_enc_find_extname(ptr, &len, rb_enc_get(path));
if (len > 0) {
RUBY_ASSERT(ext, "should point the last dot");
path = rb_str_subseq(path, 0, ext - ptr);
}
else {
/* no dot or dotted file */
path = rb_str_dup(path);
}
path = rb_str_append(path, repl);
return rb_class_new_instance(1, &path, rb_obj_class(self));
}
Returns a new pathname whose path is the path in self, after specified changes:
Pathname('t.tmp').sub_ext('.txt') # => #<Pathname:t.txt> # Extension replaced. Pathname('temp').sub_ext('.txt') # => #<Pathname:temp.txt> # Extension added. Pathname('t.tmp').sub_ext('') # => #<Pathname:t> # Extension removed.
() → untyped
Source
# File pathname_builtin.rb, line 2621 def symlink?() FileTest.symlink?(@path) end
Returns whether the entry at the path in self is a symbolic link:
# Create Pathnames. file_pn = Pathname('doc/extension.rdoc') # => #<Pathname:doc/extension.rdoc> target_pn = Pathname('..').join(file_pn) # => #<Pathname:../doc/extension.rdoc> link_pn = Pathname('lib/u.tmp') # => #<Pathname:lib/u.tmp> link_pn.symlink? # => false # Create link. link_pn.make_symlink(target_pn) link_pn.symlink? # => true link_pn.delete # Clean up.
# File pathname_builtin.rb, line 1311 def sysopen(...) File.sysopen(@path, ...) end
Opens the file at the path in self with the given mode and permissions; returns the integer file descriptor.
If the file is to be readable, it must exist; if the file is to be writable and does not exist, it is created with the given permissions:
pn = Pathname('doc/t.tmp') pn.write('foo') fd = pn.sysopen # => 5 IO.new(fd).close fd = pn.sysopen('w') # => 5 IO.new(fd).close fd = pn.sysopen('r', 0o644) # => 5 IO.new(fd).close pn.delete
Source
# File pathname_builtin.rb, line 437 def to_s @path.dup end
Returns a copy of the string path in self:
Pathname('nosuch/foo/bar').to_s # => "nosuch/foo/bar"
(Integer length) → 0
Source
# File pathname_builtin.rb, line 1899 def truncate(length) File.truncate(@path, length) end
Adjusts the size of file at the path in self to the given size; returns 0:
pn.write('0123456789') pn.size # => 10 pn.truncate(5) pn.size # => 5 pn.read # => "01234"
Pads on the right with null characters if necessary:
pn.truncate(10) pn.size # => 10 pn.read # => "01234\u0000\u0000\u0000\u0000\u0000"
() → Integer
Source
# File pathname_builtin.rb, line 2888 def unlink() Dir.unlink @path rescue Errno::ENOTDIR File.unlink @path end
Removes the entry represented by self; returns 0 if a directory, 1 if a file:
Pathname(Pathname.mktmpdir).unlink # => 0 Pathname(Tempfile.create).unlink # => 1
# File pathname_builtin.rb, line 1942 def utime(atime, mtime) File.utime(atime, mtime, @path) end
For the entry at the path in self, updates its access time to the given atime and its modification time to the given mtime; each given time may be a Time object, an integer representing a time, or nil (meaning Time.now):
pn = Pathname('doc/t.tmp') pn.write('foo') pn.stat.atime # => 1969-12-31 18:00:00 -0600 pn.stat.mtime # => 2026-07-11 16:12:15.832556524 -0500 pn.utime(0, 0) pn.stat.atime # => 1969-12-31 18:00:00 -0600 pn.stat.mtime # => 1969-12-31 18:00:00 -0600 pn.utime(nil, nil) pn.stat.atime # => 2026-07-11 16:13:06.982646673 -0500 pn.stat.mtime # => 2026-07-11 16:13:04.983530291 -0500 time = Time.now # => 2026-07-11 16:13:40.190110708 -0500 pn.utime(time, time) pn.stat.atime # => 2026-07-11 16:13:51.99317823 -0500 pn.stat.mtime # => 2026-07-11 16:13:40.190110708 -0500
Follows symbolic links:
link_pn = Pathname('link') link_pn.make_symlink(pn) link_pn.stat.atime # => 2026-07-11 16:13:51.99317823 -0500 link_pn.stat.mtime # => 2026-07-11 16:13:40.190110708 -0500 link_pn.utime(0, 0) pn.stat.atime # => 1969-12-31 18:00:00 -0600 pn.stat.mtime # => 1969-12-31 18:00:00 -0600 pn.delete link_pn.delete
() → (Integer | nil)
Source
# File pathname_builtin.rb, line 2477 def world_readable?() File.world_readable?(@path) end
If the entry at the path in self is readable by others, returns the integer permissions for the entry:
Pathname('/etc/passwd').world_readable?.to_s(8) # => "644"
Otherwise, returns nil:
pn = Pathname('doc/t.tmp') pn.write('foo') pn.chmod(0o0) pn.world_readable? # => nil pn.delete
() → (Integer | nil)
Source
# File pathname_builtin.rb, line 2665 def world_writable?() File.world_writable?(@path) end
If the entry at the path in self is writable by others, returns the integer permissions for the entry:
Pathname('/tmp').world_writable?.to_s(8) # => "777"
Otherwise, returns nil:
pn = Pathname('doc/t.tmp') pn.write('foo') pn.chmod(0o0) pn.world_writable? # => nil pn.delete
() → bool
Source
# File pathname_builtin.rb, line 2641 def writable?() FileTest.writable?(@path) end
Returns whether entry at the path in self is writable by the owner and group of the current process:
pn = Pathname('/tmp/secret.txt') pn.write('foo') pn.writable? # => true pn.chmod(0o000) pn.writable? # => false pn.delete Pathname('nosuch').writable? # => false
() → bool
Source
# File pathname_builtin.rb, line 2674 def writable_real?() FileTest.writable_real?(@path) end
Like writable?, but checks against the real user and group ids instead of the effective ids.
(String content, ?Integer offset, ?mode: Integer | String, ?flags: Integer, ?external_encoding: encoding, ?internal_encoding: encoding, ?encoding: encoding, ?textmode: boolish, ?binmode: boolish, ?autoclose: boolish) → Integer
Source
# File pathname_builtin.rb, line 1348 def write(...) File.write(@path, ...) end
Opens the file at self.to_s, writes the given data to it, and closes the file; returns the number of bytes written.
With only argument data given, writes the given data to the file:
path = 't.tmp' pn = Pathname.new(path) pn.write('foo') # => 3 File.read(path) # => "foo"
If offset is zero (the default), the file is overwritten:
pn.write('bar') File.read(path) # => "bar"
If offset in within the file content, the file is partly overwritten:
pn.write('foobarbaz') pn.write('BAR', 3) File.read(path) # => "fooBARbaz"
If offset is outside the file content, the file is padded with null characters "\u0000":
pn.write('bat', 12) File.read(path) # => "fooBARbaz\u0000\u0000\u0000bat"
Optional keyword arguments opts specify:
() → bool
Source
# File pathname_builtin.rb, line 2701 def zero?() FileTest.zero?(@path) end
Returns whether the entry represented by self exists and has size zero:
dir_pn = Pathname('example_dir') dir_pn.zero? # => false # Dir does not exist. dir_pn.mkdir dir_pn.zero? # => false # Directory never has size zero. dir_pn.empty? # => true # But this one is empty. file_pn = Pathname('example_dir/example.txt') file_pn.zero? # => false # File does not exist. file_pn.write('') file_pn.zero? # => true file_pn.write('foo') file_pn.zero? # => false file_pn.delete dir_pn.delete
Private Instance Methods
(untyped path) → untyped
Source
static VALUE
add_trailing_separator(VALUE self, VALUE path)
{
if (RSTRING_LEN(check_strpath(path)) <= 0) return path;
rb_encoding *enc = rb_enc_get(path);
const char *name = RSTRING_PTR(path);
const char *end = RSTRING_END(path);
const char *top = rb_enc_path_skip_prefix(name, end, enc);
if (top < end && isdirsep(end[-1])) {
if (end[-1] == '/' || rb_enc_prev_char(top, end, end, enc) == end - 1)
return path;
}
return rb_str_cat_cstr(rb_str_dup(path), "/");
}
add_trailing_separator(path) -> path
(untyped path) → untyped
Source
static VALUE
chop_basename(VALUE self, VALUE path)
{
long baselen, alllen = RSTRING_LEN(check_strpath(path));
if (alllen <= 0) return Qnil;
rb_encoding *enc = rb_enc_get(path);
const char *name = RSTRING_PTR(path);
const char *base = ruby_enc_find_basename(name, &baselen, &alllen, enc);
if (baselen < 1) return Qnil;
if (baselen == 1 && isdirsep(*base)) return Qnil;
RUBY_ASSERT(base >= name);
RUBY_ASSERT(base <= RSTRING_END(path));
VALUE dir = rb_str_subseq(path, 0, base - name);
VALUE basename = rb_enc_str_new(base, alllen, enc);
RB_GC_GUARD(path);
return rb_assoc_new(dir, basename);
}
chop_basename(path) -> [pre-basename, basename] or nil
(untyped path) → untyped
Source
static VALUE
has_trailing_separator(VALUE self, VALUE path)
{
long baselen, alllen = RSTRING_LEN(check_strpath(path));
if (alllen <= 0) return Qfalse;
rb_encoding *enc = rb_enc_get(path);
const char *name = RSTRING_PTR(path);
const char *base = ruby_enc_find_basename(name, &baselen, &alllen, enc);
if (baselen < 1) return Qfalse;
if (baselen == 1 && isdirsep(*base)) return Qfalse;
return RBOOL(base + alllen < RSTRING_END(path));
}
has_trailing_separator?(path) -> bool
(untyped path) → untyped
Source
static VALUE
split_names(VALUE self, VALUE path)
{
rb_encoding *enc = rb_enc_get(check_strpath(path));
const char *beg = RSTRING_PTR(path), *ptr = beg;
const char *end = RSTRING_END(path);
const char *root = rb_enc_path_skip_prefix_root(ptr, end, enc);
VALUE pre = rb_str_subseq(path, 0, root - ptr);
VALUE names = rb_ary_new();
while (ptr < end) {
const char *next = rb_enc_path_next(ptr, end, enc);
if (next > ptr) rb_ary_push(names, rb_str_subseq(path, ptr - beg, next - ptr));
ptr = next;
while (ptr < end && isdirsep(*ptr)) ++ptr;
}
return rb_assoc_new(pre, names);
}
split_names(path) -> prefix, [name, …]