Class: Pathname
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | pathname_builtin.rb, pathname.c, pathname_builtin.rb, pathname_builtin.rb, pathname_builtin.rb, pathname_builtin.rb, pathname_builtin.rb |
Overview
A Pathname object contains a string directory path or filepath; it does not represent a corresponding actual file or directory -- which in fact may or may not exist.
A Pathname object is immutable (except for method #freeze).
A pathname may be relative or absolute:
Pathname.new('lib') # => #<Pathname:lib>
Pathname.new('/usr/local/bin') # => #<Pathname:/usr/local/bin>
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
Convenience Methods
The class provides all functionality from class ::File and module ::FileTest,
along with some functionality from class ::Dir and module FileUtils.
Here's an example string path and corresponding Pathname object:
path = 'lib/fileutils.rb'
pn = Pathname.new(path) # => #<Pathname:lib/fileutils.rb>
Each of these method pairs (Pathname vs. File) gives exactly the same result:
pn.size # => 83777
File.size(path) # => 83777
pn.directory? # => false
File.directory?(path) # => false
pn.read.size # => 81074
File.read(path).size# # => 81074
Each of these method pairs gives similar results, but each Pathname method returns a more versatile Pathname object, instead of a string:
pn.dirname # => #<Pathname:lib>
File.dirname(path) # => "lib"
pn.basename # => #<Pathname:fileutils.rb>
File.basename(path) # => "fileutils.rb"
pn.split # => [#<Pathname:lib>, #<Pathname:fileutils.rb>]
File.split(path) # => ["lib", "fileutils.rb"]
Each of these methods takes a block:
pn.open do |file|
p file
end
File.open(path) do |file|
p file
end
The outputs for each:
#<File:lib/fileutils.rb (closed)>
#<File:lib/fileutils.rb (closed)>
Each of these methods takes a block:
pn.each_line do |line|
p line
break
end
File.foreach(path) do |line|
p line
break
end
The outputs for each:
"# frozen_string_literal: true\n"
"# frozen_string_literal: true\n"
More Methods
Here is a sampling of other available methods:
p1 = Pathname.new('/usr/lib') # => #<Pathname:/usr/lib>
p1.absolute? # => true
p2 = p1 + 'ruby/4.0' # => #<Pathname:/usr/lib/ruby/4.0>
p3 = p1.parent # => #<Pathname:/usr>
p4 = p2.relative_path_from(p3) # => #<Pathname:lib/ruby/4.0>
p4.absolute? # => false
p5 = Pathname.new('.') # => #<Pathname:.>
p6 = p5 + 'usr/../var' # => #<Pathname:usr/../var>
p6.cleanpath # => #<Pathname:var>
p6.realpath # => #<Pathname:/var>
p6.children.take(2)
# => [#<Pathname:usr/../var/local>, #<Pathname:usr/../var/spool>]
Breakdown of functionality
Core methods
These methods are effectively manipulating a ::String, because that's
all a path is. None of these access the file system except for
#mountpoint?, #children, #each_child, #realdirpath and #realpath.
-
- #join
- #parent
- #root?
- #absolute?
- #relative?
- #relative_path_from
- #each_filename
- #cleanpath
- #realpath
- #realdirpath
- #children
- #each_child
- #mountpoint?
File status predicate methods
These methods are a facade for ::FileTest:
- #blockdev?
- #chardev?
- #directory?
- #executable?
- #executable_real?
- #exist?
- #file?
- #grpowned?
- #owned?
- #pipe?
- #readable?
- #world_readable?
- #readable_real?
- #setgid?
- #setuid?
- #size
- #size?
- #socket?
- #sticky?
- #symlink?
- #writable?
- #world_writable?
- #writable_real?
- #zero?
File property and manipulation methods
These methods are a facade for ::File:
- #each_line(*args, &block)
- #read(*args)
- #binread(*args)
- #readlines(*args)
- #sysopen(*args)
- #write(*args)
- #binwrite(*args)
- #atime
- #birthtime
- #ctime
- #mtime
- #chmod(mode)
- #lchmod(mode)
- #chown(owner, group)
- #lchown(owner, group)
- #fnmatch(pattern, *args)
- #fnmatch?(pattern, *args)
- #ftype
- #make_link(old)
- #open(*args, &block)
- #readlink
- #rename(to)
- #stat
- #lstat
- #make_symlink(old)
- #truncate(length)
- #utime(atime, mtime)
- #lutime(atime, mtime)
- #basename(*args)
- #dirname
- #extname
- #expand_path(*args)
- #split
Directory methods
These methods are a facade for ::Dir:
Utilities
These methods are a mixture of Find, FileUtils, and others:
Method documentation
As the above section shows, most of the methods in Pathname are facades. The
documentation for these methods generally just says, for instance, "See
FileTest.writable?", as you should be familiar with the original method
anyway, and its documentation (e.g. through ri) will contain more
information. In some cases, a brief description will follow.
Constant Summary
-
VERSION =
# File 'pathname_builtin.rb', line 220
The version string.
"0.4.0"
Class Method Summary
-
.new(path) ⇒ Pathname
constructor
Returns a new Pathname object based on the given #path, via File.path(path).dup.
- .pwd
Instance Attribute Summary
-
#absolute? ⇒ Boolean
readonly
Returns whether
selfcontains an absolute path: -
#empty? ⇒ Boolean
readonly
Returns whether the entry represented by
selfexists and is empty: -
#mountpoint? ⇒ Boolean
readonly
Returns whether the path in
selfpoints to a mountpoint: -
#relative? ⇒ Boolean
readonly
The opposite of #absolute?
-
#root? ⇒ Boolean
readonly
Predicate method for root directories.
- #path readonly protected Internal use only
Instance Method Summary
- #+(other) ⇒ Pathname (also: #/)
-
#/(other)
Alias for #+.
-
#<=>(other) ⇒ 1, ...
Compares the contents of
selfandotheras strings; see String#<=>. -
#==(other) ⇒ Boolean
(also: #===, #eql?)
Returns whether the stored paths in
selfandotherare equal: -
#===(other)
Alias for #==.
-
#ascend {|entry| ... } ⇒ nil
With a block given, yields
self, then a new pathname for each successive dirname in the stored path; see File.dirname: -
#atime ⇒ Time
Returns a
::Timeobject containing the access time of the entry represented byself, as reported by the filesystem; see File System Access Time: -
#basename(path, suffix = '') ⇒ Pathname
Returns a new Pathname object containing all or part of the last entry of the path represented by
self. -
#binread(length = nil, offset = 0) ⇒ String?
Behaves like #read, except that the file is opened in binary mode with ASCII-8BIT encoding.
-
#binwrite(string, offset = 0, **opts) ⇒ nonnegative_integer
Behaves like #write, except that the file is opened in binary mode with ASCII-8BIT encoding.
-
#birthtime ⇒ Time
Returns a new
::Timeobject containing the create time of the entry represented byself; see File System Timestamps: -
#blockdev? ⇒ Boolean
Returns whether
selfrepresents a path to a block device (i.e., a direct-access device): -
#chardev? ⇒ Boolean
Returns whether
selfrepresents a path to a character device (i.e., a sequential-access device): -
#children(with_dirnames = true) ⇒ Pathname
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. -
#chmod(mode) ⇒ 1
Changes the mode (i.e., permissions) of the entry represented by
self; see File Permissions: -
#chown(owner_id, group_id) ⇒ 0
Changes the owner and group of an entry (directory or file):
-
#cleanpath(symlinks = false) ⇒ Pathname
Returns a new Pathname object, "cleaned" of unnecessary separators, single-dot entries, and double-dot entries.
-
#ctime ⇒ Time
On Windows, returns the #birthtime.
-
#delete
Alias for #unlink.
-
#descend {|entry| ... } ⇒ nil
With a block given, yields a new pathname for each successive dirname in the stored path; see File.dirname:
-
#directory? ⇒ Boolean
Returns whether the entry represented by
selfis a directory: -
#dirname
See File.dirname.
-
#each_child(with_dirnames = true) {|entry| ... } ⇒ Pathname
With a block given and
with_dirnamesgiven astrue(the default), yields a new pathname for each child of the entry represented byself; returns an array of those pathnames: -
#each_entry {|entry| ... } ⇒ nil
With a block given, yields a new pathname for each entry in the entry represented by
self; returnsnil: -
#each_filename {|component| ... } ⇒ nil
With a block given, yields each component of the string path:
-
#each_line(sep = $/, **opts) {|line| ... } → nil)
With a block given, calls the block with each line from the file represented by
self; returnsnil: -
#entries ⇒ Pathname
Returns an array of pathnames, one for each entry in the directory represented by
self: -
#eql?(other)
Alias for #==.
-
#executable? ⇒ Boolean
Returns whether the entry represented by
selfis executable; calls FileTest#executable? with argumentself.to_s: -
#executable_real? ⇒ Boolean
Returns whether the entry represented by
selfis executable by the real user and group id of the current process; calls FileTest#executable_real? with argumentself.to_s: -
#exist? ⇒ Boolean
Returns whether the entry represented by
selfexists: -
#expand_path(dirpath = '.') ⇒ Pathname
Returns a new pathname containing the absolute path for
self. -
#extname ⇒ extension
Returns the filename extension of
self-- usually the portion of the string path beginning from the last period: -
#file? ⇒ Boolean
See FileTest#file?.
-
File.fnmatch(pattern, flags = 0) ⇒ Boolean
Returns whether string
patternmatches against the string path inself, under the control of the givenflags; see Filename Matching. -
#fnmatch?(pattern) ⇒ Boolean
See File.fnmatch? (same as #fnmatch).
-
#freeze ⇒ self
Freezes
self, preventing further modifications; see Frozen Objects. -
#ftype ⇒ String
Returns the string type of the object at the path in
self: -
#glob(*args, **kwargs)
Returns or yields
Pathnameobjects. -
#grpowned?(path) ⇒ Boolean
Returns whether the filesystem entry for the path stored in
selfexists, and the effective group id of the calling process is the owner of the entry: -
#join(*objects) ⇒ Pathname
Joins the string-converted given
objectsto the string path inself; returns a new pathname containing the joined string: -
#lchmod(mode) ⇒ 1
-
#lchown(uid, gid) ⇒ 1
Not supported on some platforms (raises exception).
-
#lstat ⇒ new_stat
Returns a
::File::Statobject for the path inself; does not follow symbolic links, and therefore returns the stat object for that path, regardless of whether it is a symbolic link: -
#lutime(atime, mtime) ⇒ 1
Like #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: -
#make_link(path) ⇒ 0
Not available on some systems.
-
#make_symlink(path) ⇒ 0
Creates a symbolic link at the path in
selfto the entry at #path: -
#mkdir(permissions = 0755) ⇒ 0
Creates a directory in the underlying file system at the path in
self, with the givenpermissions; see File Permissions: -
#mkpath(permissions = 0775) ⇒ self
Creates a directory at the path in
self; creates intermediate directories as needed: -
#mtime ⇒ Time
Returns a
::Timeobject containing the time of the most recent modification to the entry represented byself; see File System Timestamps: -
#open
See File.open.
-
#opendir {|dir| ... } ⇒ Object
Creates a
::Dirobjectdirfor the directory at the path represented byself; opensdir. -
#owned? ⇒ Boolean
Returns whether the entry at the path represented by
selfexists and is owned by the user of the current process: -
#parent ⇒ Pathname
Returns a new pathname representing the parent directory of the entry represented by
self: -
#pipe? ⇒ Boolean
Returns whether the path in
selfpoints to a pipe: -
#read(length = nil, offset = 0, **opts) ⇒ String?
Reads and returns some or all of the content of the file whose path is self.to_s.
-
#readable? ⇒ Boolean
Returns whether the path in
selfpoints to an entry that is readable by the owner and group of the current process: -
#readable_real? ⇒ Boolean
Like #readable?, but checks against the real user and group ids instead of the effective ids.
-
#readlines
See
File.readlines. -
#readlink ⇒ Pathname
Returns a new pathname containing the string path to the entry referenced by
self: -
#realdirpath
Returns the real (absolute) pathname of
selfin the actual filesystem. -
#realpath
Returns the real (absolute) pathname for
selfin the actual filesystem. -
#relative_path_from(base_directory)
Returns a relative path from the given
base_directoryto the receiver. -
#rename(to)
See File.rename.
-
#rmdir
See Dir.rmdir.
-
#setgid? ⇒ Boolean
See FileTest#setgid?.
-
#setuid? ⇒ Boolean
See FileTest#setuid?.
-
#size
See FileTest#size.
-
#size? ⇒ Boolean
See FileTest#size?.
-
#socket? ⇒ Boolean
See FileTest#socket?.
-
#split
See File.split.
-
#stat
See File.stat.
-
#sticky? ⇒ Boolean
See FileTest#sticky?.
-
#sub(*args)
Return a pathname which is substituted by String#sub.
-
#sub_ext(repl)
Return a pathname with
repladded as a suffix to the basename. -
#symlink? ⇒ Boolean
Returns whether the entry at the path in
selfis a symbolic link: -
#sysopen
See
File.sysopen. -
#to_path
Alias for #to_s.
-
#to_s
(also: #to_path)
Return the path as a
::String. -
#truncate(length)
See File.truncate.
-
#unlink ⇒ 1, 0
(also: #delete)
Removes the file or directory represented by
self, using: -
#utime(atime, mtime)
See File.utime.
- #world_readable? ⇒ Boolean
- #world_writable? ⇒ Boolean
-
#writable? ⇒ Boolean
See FileTest#writable?.
- #writable_real? ⇒ Boolean
-
#write(data, offset = 0, **opts) ⇒ nonnegative_integer
Opens the file at
self.to_s, writes the givendatato it, and closes the file; returns the number of bytes written. -
#zero? ⇒ Boolean
See FileTest#zero?.
-
#add_trailing_separator(path)
private
add_trailing_separator(path) -> path.
-
#chop_basename(path)
private
chop_basename(path) -> [pre-basename, basename] or nil.
-
#has_trailing_separator?(path) ⇒ Boolean
private
has_trailing_separator?(path) -> bool.
-
#split_names(path)
private
split_names(path) -> prefix, [name, ...].
- #hash Internal use only
- #inspect Internal use only
-
#cleanpath_aggressive
private
Internal use only
Clean the path simply by resolving and removing excess
.and..entries. - #cleanpath_conservative private Internal use only
- #del_trailing_separator(path) private Internal use only
- #has_separator?(path) ⇒ Boolean private Internal use only
-
#plus(path1, path2)
private
Internal use only
(path1, path2) -> path.
- #prepend_prefix(prefix, relpath) private Internal use only
- #same_paths?(a, b) ⇒ Boolean private Internal use only
Constructor Details
.new(path) ⇒ Pathname
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:.>
Class Method Details
.pwd
[ GitHub ]# File 'pathname_builtin.rb', line 2065
alias pwd getwd
Instance Attribute Details
#absolute? ⇒ Boolean (readonly)
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.
# File 'pathname.c', line 168
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]));
}
#empty? ⇒ Boolean (readonly)
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
#mountpoint? ⇒ Boolean (readonly)
Returns whether the path in self points to a mountpoint:
Pathname('/').mountpoint? # => true
Pathname('/etc').mountpoint? # => false
Pathname('nosuch').mountpoint? # => false
#path (readonly, protected)
# File 'pathname_builtin.rb', line 224
attr_reader :path
#relative? ⇒ Boolean (readonly)
The opposite of #absolute?
It returns false if the pathname begins with a slash.
p = Pathname.new('/im/sure')
p.relative?
#=> false
p = Pathname.new('not/so/sure')
p.relative?
#=> true
# File 'pathname_builtin.rb', line 595
def relative? !absolute? end
#root? ⇒ Boolean (readonly)
Predicate method for root directories. Returns true if the
pathname consists of consecutive slashes.
It doesn't access the filesystem. So it may return false for some
pathnames which points to roots such as /usr/...
# File 'pathname.c', line 142
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);
}
Instance Method Details
#+(other) ⇒ Pathname Also known as: #/
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>
#/(other)
Alias for #+.
# File 'pathname_builtin.rb', line 754
alias / +
#<=>(other) ⇒ 1, ...
Compares the contents of self and other as strings;
see String#<=>.
Returns:
- -1 if
self's string is smaller thanother's string. - 0 if the two are equal.
- 1 if
self'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
# File 'pathname.c', line 68
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);
}
#==(other) ⇒ Boolean Also known as: #===, #eql?
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
# File 'pathname_builtin.rb', line 273
def ==(other) return false unless Pathname === other other.path == @path end
#===(other)
Alias for #==.
# File 'pathname_builtin.rb', line 277
alias === ==
#add_trailing_separator(path) (private)
add_trailing_separator(path) -> path
# File 'pathname.c', line 286
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), "/");
}
#ascend {|entry| ... } ⇒ nil
#ascend ⇒ Enumerator
nil
#ascend ⇒ Enumerator
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.
# File 'pathname_builtin.rb', line 678
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
#atime ⇒ Time
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
#basename(path, suffix = '') ⇒ Pathname
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>
#binread(length = nil, offset = 0) ⇒ String?
Behaves like #read, except that the file is opened in binary mode with ASCII-8BIT encoding.
#binwrite(string, offset = 0, **opts) ⇒ nonnegative_integer
Behaves like #write, except that the file is opened in binary mode with ASCII-8BIT encoding.
#birthtime ⇒ Time
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
#blockdev? ⇒ Boolean
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.
#chardev? ⇒ Boolean
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.
#children(with_dirnames = true) ⇒ Pathname
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.
#chmod(mode) ⇒ 1
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
#chop_basename(path) (private)
chop_basename(path) -> [pre-basename, basename] or nil
# File 'pathname.c', line 231
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);
}
#chown(owner_id, group_id) ⇒ 0
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
nilor-1owner or group id is ignored. - The method follows symbolic links to the target entry.
#cleanpath(symlinks = false) ⇒ Pathname
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>
# File 'pathname_builtin.rb', line 478
def cleanpath(consider_symlink=false) if consider_symlink cleanpath_conservative else cleanpath_aggressive end end
#cleanpath_aggressive (private)
Clean the path simply by resolving and removing excess . and .. entries.
Nothing more, nothing less.
# File 'pathname_builtin.rb', line 490
def cleanpath_aggressive # :nodoc: path = @path names = [] pre = path while r = chop_basename(pre) pre, base = r case base when '.' when '..' names.unshift base else if names[0] == '..' names.shift else names.unshift base end end end pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR if has_separator?(File.basename(pre)) names.shift while names[0] == '..' end self.class.new(prepend_prefix(pre, File.join(*names))) end
#cleanpath_conservative (private)
# File 'pathname_builtin.rb', line 516
def cleanpath_conservative # :nodoc: path = @path names = [] pre = path while r = chop_basename(pre) pre, base = r names.unshift base if base != '.' end pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR if has_separator?(File.basename(pre)) names.shift while names[0] == '..' end if names.empty? self.class.new(File.dirname(pre)) else if names.last != '..' && File.basename(path) == '.' names << '.' end result = prepend_prefix(pre, File.join(*names)) if /\A(?:\.|\.\.)\z/ !~ names.last && has_trailing_separator?(path) self.class.new(add_trailing_separator(result)) else self.class.new(result) end end end
#ctime ⇒ Time
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
#del_trailing_separator(path) (private)
# File 'pathname.c', line 302
static VALUE
del_trailing_separator(VALUE self, VALUE path)
{
long len = RSTRING_LEN(check_strpath(path));
if (len <= 0) return path;
rb_encoding *enc = rb_enc_get(path);
const char *name = RSTRING_PTR(path);
const char *end = name + len, *tail = end;
const char *top = rb_enc_path_skip_prefix(name, end, enc);
if (tail > top && isdirsep(tail[-1])) {
while (--tail > top && isdirsep(tail[-1]));
if (tail > top &&
tail[0] != '/' &&
!rb_str_enc_fastpath(path) &&
rb_enc_left_char_head(top, tail, end, enc) != tail) {
/* trailing byte, not a directory separator */
++tail;
}
if (tail < end) {
if (tail == name || (drive_letter && tail == top && top[-1] == ':')) {
++tail;
}
}
}
if (tail == end) return path;
return rb_str_subseq(path, 0, tail - name);
}
#delete
Alias for #unlink.
# File 'pathname_builtin.rb', line 2193
alias delete unlink
#descend {|entry| ... } ⇒ nil
#descend ⇒ Enumerator
nil
#descend ⇒ Enumerator
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.
# File 'pathname_builtin.rb', line 654
def descend return to_enum(__method__) unless block_given? vs = [] ascend {|v| vs << v } vs.reverse_each {|v| yield v } nil end
#directory? ⇒ Boolean
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
# File 'pathname_builtin.rb', line 1887
def directory?() FileTest.directory?(@path) end
#dirname
See File.dirname. Returns all but the last component of the path.
#each_child(with_dirnames = true) {|entry| ... } ⇒ Pathname
#each_child(with_dirnames = true) ⇒ Enumerator
Pathname
#each_child(with_dirnames = true) ⇒ Enumerator
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.
# File 'pathname_builtin.rb', line 894
def each_child(with_directory=true, &b) children(with_directory).each(&b) end
#each_entry {|entry| ... } ⇒ nil
#each_entry ⇒ Enumerator
nil
#each_entry ⇒ Enumerator
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.
#each_filename {|component| ... } ⇒ nil
#each_filename ⇒ Enumerator
nil
#each_filename ⇒ Enumerator
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.
# File 'pathname_builtin.rb', line 621
def each_filename # :yield: filename return to_enum(__method__) unless block_given? _, names = split_names(@path) names.each {|filename| yield filename } nil end
#each_line(sep = $/, **opts) {|line| ... } → nil)
#each_line(limit, **opts) {|line| ... } → nil)
#each_line(sep, limit, **opts) {|line| ... } → nil)
#each_line(...) → new_enumerator)
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)
#### =>
#### ["<a href='COPYING.ja'>日本語</a>\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.
#entries ⇒ Pathname
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>]
#eql?(other)
Alias for #==.
# File 'pathname_builtin.rb', line 278
alias eql? ==
#executable? ⇒ Boolean
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
# File 'pathname_builtin.rb', line 1823
def executable?() FileTest.executable?(@path) end
#executable_real? ⇒ Boolean
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
# File 'pathname_builtin.rb', line 1842
def executable_real?() FileTest.executable_real?(@path) end
#exist? ⇒ Boolean
Returns whether the entry represented by self exists:
Pathname('.').exist? # => true
Pathname('README.md').exist? # => true
Pathname('nosuch').exist? # => false
#expand_path(dirpath = '.') ⇒ Pathname
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'). # => #<Pathname:/snap/README>
Pathname('bin'). # => #<Pathname:/snap/bin>
Pathname('bin/../var'). # => #<Pathname:/snap/var> # Cleaned.
#### Other dirpath.
Pathname('../zip').('/usr/bin/ruby') # => #<Pathname:/usr/bin/zip>
Dir.chdir('/usr/bin')
Pathname('../../snap').(__FILE__) # => #<Pathname:/usr/snap>
Evaluates an absolute path without respect to dirpath:
Pathname('/snap'). # => #<Pathname:/snap>
Pathname('/snap')..('nosuch') # => #<Pathname:/snap>
Pathname('/snap/../snap'). # => #<Pathname:/snap> # Cleaned.
More examples:
{Dir.chdir}('/usr/bin')
Pathname('../../snap').(__FILE__) # => {#<}Pathname:/usr/snap>
Pathname('../../snap'). # => {#<}Pathname:/snap>
#extname ⇒ extension
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.
#file? ⇒ Boolean
See FileTest#file?.
File.fnmatch(pattern, flags = 0) ⇒ Boolean
Returns whether string pattern matches against the string path in self,
under the control of the given flags;
see Filename Matching.
#fnmatch?(pattern) ⇒ Boolean
See File.fnmatch? (same as #fnmatch).
# File 'pathname_builtin.rb', line 1410
def fnmatch?(pattern, ...) File.fnmatch?(pattern, @path, ...) end
#freeze ⇒ self
Freezes self, preventing further modifications;
see Frozen Objects.
# File 'pathname_builtin.rb', line 254
def freeze super @path.freeze self end
#ftype ⇒ String
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.
#glob(*args, **kwargs)
Returns or yields Pathname objects.
Pathname("ruby-2.4.2").glob("R*.md") #=> [#Pathname:ruby-2.4.2/README.md, #Pathname:ruby-2.4.2/README.ja.md]
See Dir.glob.
This method uses the base keyword argument of Dir.glob.
#grpowned?(path) ⇒ Boolean
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.
#has_separator?(path) ⇒ Boolean (private)
# File 'pathname.c', line 182
static VALUE
has_separator_p(VALUE self, VALUE path)
{
const char *ptr = RSTRING_PTR(check_strpath(path));
const char *end = RSTRING_END(path);
if (alt_separator) {
rb_encoding *enc = rb_enc_get(path);
bool mb = !rb_str_enc_fastpath(path);
while (ptr < end) {
if (isdirsep(*ptr)) return Qtrue;
ptr += (mb ? rb_enc_mbclen(ptr, end, enc) : 1);
}
}
else {
/* assume '/' will never be trailing bytes */
if (memchr(ptr, '/', end - ptr)) return Qtrue;
}
return Qfalse;
}
#has_trailing_separator?(path) ⇒ Boolean (private)
has_trailing_separator?(path) -> bool
# File 'pathname.c', line 271
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));
}
#hash
# File 'pathname_builtin.rb', line 280
def hash # :nodoc: @path.hash end
#inspect
# File 'pathname_builtin.rb', line 292
def inspect # :nodoc: "#<#{self.class}:#{@path}>" end
#join(*objects) ⇒ Pathname
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 810
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
#lchmod(mode) ⇒ 1
- Not supported on some platforms (raises Errno
ENOTSUP).
When supported: like #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')
#lchown(uid, gid) ⇒ 1
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')
#lstat ⇒ new_stat
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')
#lutime(atime, mtime) ⇒ 1
Like #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.
#make_link(path) ⇒ 0
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.
#make_symlink(path) ⇒ 0
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.
#mkdir(permissions = 0755) ⇒ 0
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.
#mkpath(permissions = 0775) ⇒ self
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.
Directories are created with the given permissions; see File Permissions. The permissions for already-existing directories are not changed.
# File 'pathname_builtin.rb', line 315
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
#mtime ⇒ Time
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
#open
See File.open. Opens the file for reading or writing.
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
#owned? ⇒ Boolean
#parent ⇒ Pathname
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>
# File 'pathname_builtin.rb', line 557
def parent self + '..' end
#pipe? ⇒ Boolean
#plus(path1, path2) (private)
(path1, path2) -> path
# File 'pathname_builtin.rb', line 757
def plus(path1, path2) # :nodoc: prefix2 = path2 index_list2 = [] basename_list2 = [] while r2 = chop_basename(prefix2) prefix2, basename2 = r2 index_list2.unshift prefix2.length basename_list2.unshift basename2 end return path2 if prefix2 != '' prefix1 = path1 while true while !basename_list2.empty? && basename_list2.first == '.' index_list2.shift basename_list2.shift end break unless r1 = chop_basename(prefix1) prefix1, basename1 = r1 next if basename1 == '.' if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..' prefix1 = prefix1 + basename1 break end index_list2.shift basename_list2.shift end r1 = chop_basename(prefix1) if !r1 && (r1 = has_separator?(File.basename(prefix1))) while !basename_list2.empty? && basename_list2.first == '..' index_list2.shift basename_list2.shift end end if !basename_list2.empty? suffix2 = path2[index_list2.first..-1] r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2 else r1 ? prefix1 : File.dirname(prefix1) end end
#prepend_prefix(prefix, relpath) (private)
# File 'pathname_builtin.rb', line 339
def prepend_prefix(prefix, relpath) # :nodoc: if relpath.empty? File.dirname(prefix) elsif has_separator?(prefix) add_trailing_separator(File.dirname(prefix)) + relpath else prefix + relpath end end
#read(length = nil, offset = 0, **opts) ⇒ String?
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:
#readable? ⇒ Boolean
#readable_real? ⇒ Boolean
Like #readable?, but checks against the real user and group ids instead of the effective ids.
# File 'pathname_builtin.rb', line 1966
def readable_real?() FileTest.readable_real?(@path) end
#readlines
See File.readlines. Returns all the lines from the file.
#readlink ⇒ Pathname
Returns a new pathname containing the string path to the entry referenced 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
#realdirpath
Returns the real (absolute) pathname of self in the actual filesystem.
Does not contain symlinks or useless dots, .. and ..
The last component of the real pathname can be nonexistent.
# File 'pathname_builtin.rb', line 1731
def realdirpath(...) self.class.new(File.realdirpath(@path, ...)) end
#realpath
Returns the real (absolute) pathname for self in the actual filesystem.
Does not contain symlinks or useless dots, .. and ..
All components of the pathname must exist when this method is called.
#relative_path_from(base_directory)
Returns a relative path from the given base_directory to the receiver.
If self is absolute, then base_directory must be absolute too.
If self is relative, then base_directory must be relative too.
This method doesn't access the filesystem. It assumes no symlinks.
::ArgumentError is raised when it cannot find a relative path.
Note that this method does not handle situations where the case sensitivity of the filesystem in use differs from the operating system default.
# File 'pathname_builtin.rb', line 912
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
#rename(to)
See File.rename. Rename the file.
#rmdir
See Dir.rmdir. Remove the referenced directory.
#same_paths?(a, b) ⇒ Boolean (private)
# File 'pathname.c', line 124
static VALUE
same_paths(VALUE self, VALUE a, VALUE b)
{
check_strpath(a);
check_strpath(b);
if (CASEFOLD_FILESYSTEM)
return RBOOL(rb_str_casecmp(a, b) == INT2FIX(0));
else
return rb_str_equal(a, b);
}
#setgid? ⇒ Boolean
See FileTest#setgid?.
#setuid? ⇒ Boolean
See FileTest#setuid?.
#size
See FileTest#size.
#size? ⇒ Boolean
See FileTest#size?.
#socket? ⇒ Boolean
See FileTest#socket?.
#split
See File.split. Returns the #dirname and the #basename in an
::Array.
#split_names(path) (private)
split_names(path) -> prefix, [name, ...]
# File 'pathname.c', line 251
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);
}
#stat
See File.stat. Returns a ::File::Stat object.
#sticky? ⇒ Boolean
See FileTest#sticky?.
#sub(*args)
Return a pathname which is substituted by String#sub.
path1 = Pathname.new('/usr/bin/perl')
path1.sub('perl', 'ruby')
#=> #<Pathname:/usr/bin/ruby>
# File 'pathname.c', line 109
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));
}
#sub_ext(repl)
Return a pathname with repl added as a suffix to the basename.
If self has no extension part, repl is appended.
Pathname.new('/usr/bin/shutdown').sub_ext('.rb')
#=> #<Pathname:/usr/bin/shutdown.rb>
# File 'pathname.c', line 210
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));
}
#symlink? ⇒ Boolean
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.
#sysopen
See File.sysopen.
#to_path
Alias for #to_s.
# File 'pathname_builtin.rb', line 290
alias to_path to_s
#to_s Also known as: #to_path
Return the path as a ::String.
# File 'pathname_builtin.rb', line 285
def to_s @path.dup end
#truncate(length)
See File.truncate. Truncate the file to length bytes.
#unlink ⇒ 1, 0 Also known as: #delete
Removes the file or directory represented by self, using:
- File.unlink, if
selfrepresents a file; returns1. - Dir.unlink, if
selfrepresents a directory; returns0.
Examples:
Pathname(Tempfile.create).unlink # => 1
Pathname(Pathname.mktmpdir).unlink # => 0
#utime(atime, mtime)
See File.utime. Update the access and modification times.
#world_readable? ⇒ Boolean
[ GitHub ]
# File 'pathname_builtin.rb', line 1956
def world_readable?() File.world_readable?(@path) end
#world_writable? ⇒ Boolean
[ GitHub ]
# File 'pathname_builtin.rb', line 2007
def world_writable?() File.world_writable?(@path) end
#writable? ⇒ Boolean
See FileTest#writable?.
#writable_real? ⇒ Boolean
[ GitHub ]
# File 'pathname_builtin.rb', line 2010
def writable_real?() FileTest.writable_real?(@path) end
#write(data, offset = 0, **opts) ⇒ nonnegative_integer
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:
#zero? ⇒ Boolean
See FileTest#zero?.