Class: File::Stat
| Relationships & Source Files | |
| Super Chains via Extension / Inclusion / Inheritance | |
|
Instance Chain:
self,
::Comparable
|
|
| Inherits: | Object |
| Defined in: | file.c, file.c |
Overview
A File::Stat object contains information about an entry in the filesystem.
Each of these methods returns a new File::Stat object. the first three follow symbolic links; the others don't:
Snapshot
A new File::Stat object takes an immediate "snapshot" of the filesystem entry at the given 'path'; the snapshot is never updated, regardless of changes in the entry (even its deletion):
filepath = '/tmp/t.tmp'
File.stat(filepath) # Raises Errno::ENOENT: No such file or directory.
File.write(filepath, 'foo')
stat = File.stat(filepath)
stat.birthtime # => 2026-10-03 12:19:23.723062465 -0500
File.delete(filepath)
stat.birthtime # => 2026-10-03 12:19:23.723062465 -0500
Filesystem Dependencies
Methods in a File::Stat object may return filesystem-dependent values,
and not all values are meaningful on all filesystems;
for example, #blocks returns nil on a Windows filesystem,
but returns an integer on others.
See also Kernel.test.
Class Method Summary
- .new(path) ⇒ Stat constructor
Instance Attribute Summary
-
#blockdev? ⇒ Boolean
readonly
Returns whether the entry in thesnapshot in
selfis a block device (i.e., a direct-access device): -
#chardev? ⇒ Boolean
readonly
Returns whether the entry in thesnapshot in
selfis a character device (i.e., a sequential-access device): -
#directory? ⇒ Boolean
readonly
Returns whether the entry in thesnapshot in
selfis a directory: -
#executable? ⇒ Boolean
readonly
Returns whether the filesystem entry represented by
selfisexecutable by the effective owner/group of the current process: -
#executable_real? ⇒ Boolean
readonly
Like Pathname#executable?, but checks against the real owner/group instead of the effective owner/group.
-
#file? ⇒ Boolean
readonly
Returns whether
selfrepresents a filesystem entry that exists and is a regular file; see ftype: -
#grpowned?(path) ⇒ Boolean
readonly
Returns
falseon Windows. -
#owned? ⇒ Boolean
readonly
Returns whether
selfrepresents a filesystem entry that, at the timeselfwas created, existed and was owned by the user of the current process; seeSnapshot: -
#pipe? ⇒ Boolean
readonly
Returns whether the entry at the path in
selfis a pipe: -
#readable? ⇒ Boolean
readonly
Returns whether the entry represented by
selfexists and is readable by the owner and group of the current process; seePermissions: -
#readable_real? ⇒ Boolean
readonly
Like #readable?, but checks against the real user and group ids instead of the effective ids.
-
#setgid? ⇒ Boolean
readonly
Returns whether the setgid bit is set in thespecial bits for the entry represented in
self: -
#setuid? ⇒ Boolean
readonly
Returns whether the setuid bit is set in thespecial bits for the entry represented in
self: -
#size ⇒ Integer
readonly
Returns the size of
selfin bytes: -
#size? ⇒ Boolean
readonly
Returns the size in bytes of the entry in
selfif the entry exists and has non-zero size,nilotherwise: -
#socket? ⇒ Boolean
readonly
Returns whether entry in
selfis a socket: -
#sticky? ⇒ Boolean
readonly
Returns whether the sticky bit is set in thespecial bits for
self: -
#symlink? ⇒ Boolean
readonly
Returns whether the entry in
self(seeSnapshot) is asymbolic link: -
#world_readable? ⇒ Boolean
readonly
If the entry in
selfexists and is readable by others, returns the integerpermissions for the entry; otherwise, returnsnil: -
#world_writable? ⇒ Boolean
readonly
If the entry in
selfexists and is writable by others, returns the integerpermissions for the entry; otherwise, returnsnil: -
#writable? ⇒ Boolean
readonly
Returns whether the entry at the path in
selfexists and is writable by the effective owner and group in the current process: -
#writable_real? ⇒ Boolean
readonly
Like File::Stat.writable?, but checks against the real owner and group instead of the effective owner and group.
-
#zero? ⇒ Boolean
readonly
Returns whether the entry at the path in
selfhas size zero.
Instance Method Summary
-
#<=>(other) ⇒ 1, ...
Compares thesnapshots in
selfandother, by comparing their modification timesself.mtimeandother.mtime. -
#atime ⇒ Time
Returns a new
::Timeobject containing the access time of thesnapshot inself. See File System Timestamps. -
#birthtime ⇒ Time
Returns a new
::Timeobject containing the creation time of thesnapshot inself. See File System Timestamps: -
#blksize ⇒ Integer?
Returns the block size of the filesystem of thesnapshot in
self, ornilon Windows: -
#blocks ⇒ Integer?
Returns the number of filesystem blocks allocated for the entry in thesnapshot in
self: -
#ctime ⇒ Time
On Windows, returns the birthtime for the entry in
self. -
#dev ⇒ Integer
Returns the device number for thesnapshot in
self: -
#dev_major ⇒ Integer
Returns
nilon Windows. -
#dev_minor ⇒ Integer
Returns
nilon Windows. -
#ftype ⇒ String
Returns the string type of the path in thesnapshot in
self, one of: -
#gid ⇒ Integer
On Windows, returns
0. -
#ino ⇒ Integer
Returns the inode value for the entry in thesnapshot in
self: -
#inspect ⇒ String
Returns a string representation of the entry in thesnapshot in
self; here we split the returned strings for readability: -
#mode ⇒ Integer
Returns an integer representing the permissions of the entry in thesnapshot in
self; seeFilesystem Modes. - #mtime ⇒ Time
-
#nlink ⇒ Integer
Returns the number of hard links to stat.
-
#rdev ⇒ Integer?
Returns an integer representing the device type on which stat resides.
-
#rdev_major ⇒ Integer
Returns the major part of #rdev or
nil. -
#rdev_minor ⇒ Integer
Returns the minor part of #rdev or
nil. -
#uid ⇒ Integer
Returns the numeric user id of the owner of stat.
- #initialize_copy(orig) Internal use only
::Comparable - Included
| #< | Returns whether |
| #<= | Returns whether |
| #== | Compares two objects based on the receiver's #<=> method, returning true if it returns 0. |
| #> | Returns whether |
| #>= | Returns whether |
| #between? | |
| #clamp |
Constructor Details
.new(path) ⇒ Stat
# File 'file.c', line 6994
static VALUE
rb_stat_init(VALUE obj, VALUE fname)
{
rb_io_stat_data st;
FilePathValue(fname);
fname = rb_str_encode_ospath(fname);
if (STATX(StringValueCStr(fname), &st, STATX_ALL) == -1) {
rb_sys_fail_path(fname);
}
struct rb_stat *rb_st;
TypedData_Get_Struct(obj, struct rb_stat, &stat_data_type, rb_st);
rb_st->stat = st;
rb_st->initialized = true;
return Qnil;
}
Instance Attribute Details
#blockdev? ⇒ Boolean (readonly)
Returns whether the entry in thesnapshot in self
is a block device (i.e., a direct-access device):
File.stat('/dev/nvme0n1').blockdev? # => true
File.stat('/dev/loop0').blockdev? # => true
File.stat('/dev/tty').blockdev? # => false
File.stat('/dev/null').blockdev? # => false
The returned value is filesystem-dependent; returns false on Windows or MacOS.
# File 'file.c', line 7218
static VALUE
rb_stat_b(VALUE obj)
{
#ifdef S_ISBLK
if (S_ISBLK(get_stat(obj)->ST_(mode))) return Qtrue;
#endif
return Qfalse;
}
#chardev? ⇒ Boolean (readonly)
Returns whether the entry in thesnapshot in self
is a character device (i.e., a sequential-access device):
File.stat('/dev/tty').chardev? # => true
File.stat('/dev/null').chardev? # => true
File.stat('/dev/nvme0n1').chardev? # => false
File.stat('/dev/loop0').chardev? # => false
The returned value is filesystem-dependent; on Windows, always false.
# File 'file.c', line 7247
static VALUE
rb_stat_c(VALUE obj)
{
if (S_ISCHR(get_stat(obj)->ST_(mode))) return Qtrue;
return Qfalse;
}
#directory? ⇒ Boolean (readonly)
Returns whether the entry in thesnapshot in self
is a directory:
File.stat('/etc').directory? # => true
File.stat('/etc/passwd').directory? # => false
# File 'file.c', line 7093
static VALUE
rb_stat_d(VALUE obj)
{
if (S_ISDIR(get_stat(obj)->ST_(mode))) return Qtrue;
return Qfalse;
}
#executable? ⇒ Boolean (readonly)
Returns whether the filesystem entry represented by self
isexecutable
by the effective owner/group of the current process:
File.stat('/bin/ruby').executable? # => true
File.stat('/etc').executable? # => true
File.stat('/etc/passwd').executable? # => false
Raises Errno::ENOENT if the entry does not exist.
Note that some filesystem settings may cause this method to return true
even though the entry is not executable by the effective owner/group.
# File 'file.c', line 7575
static VALUE
rb_stat_x(VALUE obj)
{
rb_io_stat_data *st = get_stat(obj);
#ifdef USE_GETEUID
if (geteuid() == 0) {
return RBOOL(st->ST_(mode) & S_IXUGO);
}
#endif
#ifdef S_IXUSR
if (rb_stat_owned(obj))
return RBOOL(st->ST_(mode) & S_IXUSR);
#endif
#ifdef S_IXGRP
if (rb_stat_grpowned(obj))
return RBOOL(st->ST_(mode) & S_IXGRP);
#endif
#ifdef S_IXOTH
if (!(st->ST_(mode) & S_IXOTH)) return Qfalse;
#endif
return Qtrue;
}
#executable_real? ⇒ Boolean (readonly)
Like Pathname#executable?, but checks against the real owner/group instead of the effective owner/group.
# File 'file.c', line 7608
static VALUE
rb_stat_X(VALUE obj)
{
rb_io_stat_data *st = get_stat(obj);
#ifdef USE_GETEUID
if (getuid() == 0) {
return RBOOL(st->ST_(mode) & S_IXUGO);
}
#endif
#ifdef S_IXUSR
if (rb_stat_rowned(obj))
return RBOOL(st->ST_(mode) & S_IXUSR);
#endif
#ifdef S_IXGRP
if (rb_group_member(get_stat(obj)->ST_(gid)))
return RBOOL(st->ST_(mode) & S_IXGRP);
#endif
#ifdef S_IXOTH
if (!(st->ST_(mode) & S_IXOTH)) return Qfalse;
#endif
return Qtrue;
}
#file? ⇒ Boolean (readonly)
# File 'file.c', line 7648
static VALUE
rb_stat_f(VALUE obj)
{
if (S_ISREG(get_stat(obj)->ST_(mode))) return Qtrue;
return Qfalse;
}
#grpowned?(path) ⇒ Boolean (readonly)
Returns false on Windows.
On other systems, returns whether the effective group id of the calling process
is the owner of thesnapshot in self:
filepath = '/tmp/t.tmp'
File.write(filepath, 'foo')
File.stat(filepath).grpowned? # => true
File.stat('/etc').grpowned? # => false
File.stat('/etc/passwd').grpowned? # => false
File.delete(filepath) # Clean up.
# File 'file.c', line 7320
static VALUE
rb_stat_grpowned(VALUE obj)
{
#ifndef _WIN32
if (rb_group_member(get_stat(obj)->ST_(gid))) return Qtrue;
#endif
return Qfalse;
}
#owned? ⇒ Boolean (readonly)
Returns whether self represents a filesystem entry that,
at the time self was created,
existed and was owned by the user of the current process;
seeSnapshot:
filepath = 'doc/t.tmp'
File.write(filepath, 'foo')
filestat = File.stat(filepath)
filestat.owned? # => true
File.delete(filepath)
filestat.owned? # => true # Snapshot unchanged.
dirpath = 'doc/tmp'
Dir.mkdir(dirpath)
dirstat = File.stat(dirpath)
dirstat.owned? # => true
Dir.rmdir(dirpath)
dirstat.owned? # => true # Snapshot unchanged.
File.stat('/etc').owned? # => false
# File 'file.c', line 7284
static VALUE
rb_stat_owned(VALUE obj)
{
if (get_stat(obj)->ST_(uid) == geteuid()) return Qtrue;
return Qfalse;
}
#pipe? ⇒ Boolean (readonly)
# File 'file.c', line 7119
static VALUE
rb_stat_p(VALUE obj)
{
#ifdef S_IFIFO
if (S_ISFIFO(get_stat(obj)->ST_(mode))) return Qtrue;
#endif
return Qfalse;
}
#readable? ⇒ Boolean (readonly)
Returns whether the entry represented by self
exists and is readable by the owner and group of the current process;
seePermissions:
path = '/tmp/secret.txt'
File.write(path, 'foo')
File.stat(path).readable? # => true
File.chmod(0o000, path)
File.stat(path).readable? # => false
File.delete(path) # Clean up.
# File 'file.c', line 7350
static VALUE
rb_stat_r(VALUE obj)
{
rb_io_stat_data *st = get_stat(obj);
#ifdef USE_GETEUID
if (geteuid() == 0) return Qtrue;
#endif
#ifdef S_IRUSR
if (rb_stat_owned(obj))
return RBOOL(st->ST_(mode) & S_IRUSR);
#endif
#ifdef S_IRGRP
if (rb_stat_grpowned(obj))
return RBOOL(st->ST_(mode) & S_IRGRP);
#endif
#ifdef S_IROTH
if (!(st->ST_(mode) & S_IROTH)) return Qfalse;
#endif
return Qtrue;
}
#readable_real? ⇒ Boolean (readonly)
Like #readable?, but checks against the real user and group ids instead of the effective ids.
# File 'file.c', line 7382
static VALUE
rb_stat_R(VALUE obj)
{
rb_io_stat_data *st = get_stat(obj);
#ifdef USE_GETEUID
if (getuid() == 0) return Qtrue;
#endif
#ifdef S_IRUSR
if (rb_stat_rowned(obj))
return RBOOL(st->ST_(mode) & S_IRUSR);
#endif
#ifdef S_IRGRP
if (rb_group_member(get_stat(obj)->ST_(gid)))
return RBOOL(st->ST_(mode) & S_IRGRP);
#endif
#ifdef S_IROTH
if (!(st->ST_(mode) & S_IROTH)) return Qfalse;
#endif
return Qtrue;
}
#setgid? ⇒ Boolean (readonly)
Returns whether the setgid bit is set
in thespecial bits
for the entry represented in self:
path = '/tmp/t.tmp'
File.write(path, 'foo')
stat = File.stat(path) # Take a snapshot.
stat.setgid? # => false
stat.mode.to_s(8) # => "100664"
File.chmod(0o2644, path) # Set the bit; stat snapshot unchanged.
stat.setgid? # => false
stat.mode.to_s(8) # => "100664"
stat = File.stat(path) # Fresh stat; snapshot changed.
stat.setgid? # => true
stat.mode.to_s(8) # => "102644"
File.delete(path) # Clean up.
On Windows, the bit is never set; the method always returns false.
# File 'file.c', line 7799
static VALUE
rb_stat_sgid(VALUE obj)
{
#ifdef S_ISGID
if (get_stat(obj)->ST_(mode) & S_ISGID) return Qtrue;
#endif
return Qfalse;
}
#setuid? ⇒ Boolean (readonly)
Returns whether the setuid bit is set
in thespecial bits
for the entry represented in self:
path = '/tmp/t.tmp'
File.write(path, 'foo')
stat = File.stat(path) # Take snapshot; bit not set.
stat.setuid? # => false
stat.mode.to_s(8) # => "100664"
File.chmod(0o4644, path) # Set the bit; snapshot not updated.
stat.setuid? # => false
stat.mode.to_s(8) # => "100664"
stat = File.stat(path) # Fresh snapshot.
stat.setuid? # => true
stat.mode.to_s(8) # => "104644"
File.delete(path) # Clean up.
On Windows, the bit is never set; the method always returns false.
# File 'file.c', line 7762
static VALUE
rb_stat_suid(VALUE obj)
{
#ifdef S_ISUID
if (get_stat(obj)->ST_(mode) & S_ISUID) return Qtrue;
#endif
return Qfalse;
}
#size ⇒ Integer (readonly)
Returns the size of self in bytes:
File.stat('doc/maintainers.md').size # => 14900 # Regular file.
File.stat('doc/syntax/').size # => 4096 # Directory.
#### When the file size changes.
path = '/tmp/t.tmp'
file = File.new(path, 'w+')
file.write('foo')
stat = File.stat(path) # Take snapshot.
stat.size # => 3
file.write('bar') # Change file size.
file.size # => 6
stat.size # => 3 # Snapshot unchanged.
stat = File.stat(path) # Fresh snapshot.
stat.size # => 6 # Shapshot different.
#### Clean up.
file.close
File.delete(path)
# File 'file.c', line 989
static VALUE
rb_stat_size(VALUE self)
{
return OFFT2NUM(get_stat(self)->ST_(size));
}
#size? ⇒ Boolean (readonly)
Returns the size in bytes of the entry in self
if the entry exists and has non-zero size, nil otherwise:
path = '/tmp/t.tmp'
File.write(path, 'foo')
File.size(path) # => 3
stat = File.stat(path) # Take snapshot.
stat.size? # => 3 # Non-zero size.
File.write(path, '')
File.size(path) # => 0
stat.size? # => 3 # Snapshot unchanged.
stat = File.stat(path) # Take new snapshot.
stat.size? # => nil # Zero size
File.delete(path) # Clean up.
# File 'file.c', line 7725
static VALUE
rb_stat_s(VALUE obj)
{
rb_off_t size = get_stat(obj)->ST_(size);
if (size == 0) return Qnil;
return OFFT2NUM(size);
}
#socket? ⇒ Boolean (readonly)
Returns whether entry in self is a socket:
sock_path = '/tmp/socket'
server = UNIXServer.new(sock_path)
stat = File.stat(sock_path)
stat.socket? # => true
File.delete(sock_path) # Clean up.
stat.socket? # => true
file_path = '/etc/passwd'
File.exist?(file_path) # => true # Snapshot not updated.
stat = File.stat(file_path)
stat.socket? # => false
# File 'file.c', line 7188
static VALUE
rb_stat_S(VALUE obj)
{
#ifdef S_ISSOCK
if (S_ISSOCK(get_stat(obj)->ST_(mode))) return Qtrue;
#endif
return Qfalse;
}
#sticky? ⇒ Boolean (readonly)
Returns whether the sticky bit is set
in thespecial bits
for self:
filepath = '/tmp/t.tmp'
File.write(filepath, 'foo')
stat = File.stat(filepath)
stat.sticky? # => false
stat.mode.to_s(8) # => "100664"
File.chmod(01644, filepath) # => 1 # Stat unchanged.
stat.sticky? # => false
stat.mode.to_s(8) # => "100664"
stat = File.stat(filepath) # Fresh stat.
stat.sticky? # => true
stat.mode.to_s(8) # => "101644"
File.delete(filepath) # Clean up.
Returns false on Windows.
# File 'file.c', line 7836
static VALUE
rb_stat_sticky(VALUE obj)
{
#ifdef S_ISVTX
if (get_stat(obj)->ST_(mode) & S_ISVTX) return Qtrue;
#endif
return Qfalse;
}
#symlink? ⇒ Boolean (readonly)
Returns whether the entry in self (seeSnapshot)
is asymbolic link:
filepath = '/etc/passwd'
linkpath = '/tmp/foo'
File.symlink(filepath, linkpath)
File.stat(filepath).symlink? # => false
File.lstat(filepath).symlink? # => false
stat = File.stat(linkpath) # Snapshot with stat follows link.
stat.symlink? # => false
lstat = File.lstat(linkpath) # Snapshot with lstat does not follow link.
lstat.symlink? # => true
File.delete(linkpath) # Clean up.
#### Snapshots are unchanged, even when link deleted.
stat.symlink? # => false
lstat.symlink? # => true
# File 'file.c', line 7156
static VALUE
rb_stat_l(VALUE obj)
{
#ifdef S_ISLNK
if (S_ISLNK(get_stat(obj)->ST_(mode))) return Qtrue;
#endif
return Qfalse;
}
#world_readable? ⇒ Boolean (readonly)
If the entry in self exists and is readable by others,
returns the integerpermissions
for the entry;
otherwise, returns nil:
filepath = '/tmp/t.tmp'
File.write(filepath, 'foo')
File.stat(filepath).world_readable?.to_s(8) # => "664" # World-readable.
File.chmod(0o000, filepath) # Make unreadable.
File.stat(filepath).world_readable? # => nil # Not world-readable.
File.delete(filepath) # Clean up.
File.stat('.').world_readable?.to_s(8) # => "775" # Directory.
# File 'file.c', line 7426
static VALUE
rb_stat_wr(VALUE obj)
{
#ifdef S_IROTH
rb_io_stat_data *st = get_stat(obj);
if ((st->ST_(mode) & (S_IROTH)) == S_IROTH) {
return UINT2NUM(st->ST_(mode) & (S_IRUGO|S_IWUGO|S_IXUGO));
}
#endif
return Qnil;
}
#world_writable? ⇒ Boolean (readonly)
If the entry in self exists and is writable by others,
returns the integerpermissions
for the entry;
otherwise, returns nil:
filepath = '/tmp/t.tmp'
File.write(filepath, 'foo')
File.stat(filepath).world_writable? # => nil # Not world-writable.
File.chmod(0o777, filepath) # Make world-writable.
File.stat(filepath).world_writable?.to_s(8) # => "777" # World-writable.
File.delete(filepath) # Clean up.
File.stat('/tmp').world_writable?.to_s(8) # => "777" # Directory.
# File 'file.c', line 7541
static VALUE
rb_stat_ww(VALUE obj)
{
#ifdef S_IWOTH
rb_io_stat_data *st = get_stat(obj);
if ((st->ST_(mode) & (S_IWOTH)) == S_IWOTH) {
return UINT2NUM(st->ST_(mode) & (S_IRUGO|S_IWUGO|S_IXUGO));
}
#endif
return Qnil;
}
#writable? ⇒ Boolean (readonly)
Returns whether the entry at the path in self exists and is writable
by the effective owner and group in the current process:
filepath = '/tmp/secret.txt'
File.write(filepath, 'foo')
File.stat(filepath).writable? # => true # Writable.
File.chmod(0o000, filepath) # Make non-writable.
File.stat(filepath).writable? # => false # Not writable.
File.delete(filepath) # Clean up.
File.stat('/etc').writable? # => false # Directory.
Note that filesystem security features may cause this method to return true
even when the file is not writable by the effective owner and group.
# File 'file.c', line 7461
static VALUE
rb_stat_w(VALUE obj)
{
rb_io_stat_data *st = get_stat(obj);
#ifdef USE_GETEUID
if (geteuid() == 0) return Qtrue;
#endif
#ifdef S_IWUSR
if (rb_stat_owned(obj))
return RBOOL(st->ST_(mode) & S_IWUSR);
#endif
#ifdef S_IWGRP
if (rb_stat_grpowned(obj))
return RBOOL(st->ST_(mode) & S_IWGRP);
#endif
#ifdef S_IWOTH
if (!(st->ST_(mode) & S_IWOTH)) return Qfalse;
#endif
return Qtrue;
}
#writable_real? ⇒ Boolean (readonly)
Like File::Stat.writable?, but checks against the real owner and group instead of the effective owner and group.
Note that filesystem security features may cause this method to return true
even when the entry in self is not writable by the real owner and group.
# File 'file.c', line 7496
static VALUE
rb_stat_W(VALUE obj)
{
rb_io_stat_data *st = get_stat(obj);
#ifdef USE_GETEUID
if (getuid() == 0) return Qtrue;
#endif
#ifdef S_IWUSR
if (rb_stat_rowned(obj))
return RBOOL(st->ST_(mode) & S_IWUSR);
#endif
#ifdef S_IWGRP
if (rb_group_member(get_stat(obj)->ST_(gid)))
return RBOOL(st->ST_(mode) & S_IWGRP);
#endif
#ifdef S_IWOTH
if (!(st->ST_(mode) & S_IWOTH)) return Qfalse;
#endif
return Qtrue;
}
#zero? ⇒ Boolean (readonly)
Returns whether the entry at the path in self has size zero.
The entry may be a file:
filepath = '/tmp/t.tmp'
File.write(filepath, 'foo')
File.stat(filepath).zero? # => false
File.truncate(filepath, 0)
File.stat(filepath).zero? # => true
File.delete(filepath) # Clean up.
The entry may be a directory:
dirpath = '/tmp/foo'
Dir.mkdir(dirpath)
stat = File.stat(dirpath)
#### Size is filesystem-dependent; may or may not be zero.
stat.size # => 4096
stat.zero? # => false
filepath = File.join(dirpath, 't.tmp') # => "/tmp/foo/t.tmp"
File.write(filepath, 'foo')
stat = File.stat(dirpath)
stat.size # => 4096
stat.zero? # => false
FileUtils.rm_rf(dirpath) # Clean up.
# File 'file.c', line 7693
static VALUE
rb_stat_z(VALUE obj)
{
if (get_stat(obj)->ST_(size) == 0) return Qtrue;
return Qfalse;
}
Instance Method Details
#<=>(other) ⇒ 1, ...
Compares thesnapshots
in self and other, by comparing their modification times
self.mtime and other.mtime.
Returns:
-1, ifself.mtimeis earlier.0, if the two values are equal.1, ifself.mtimeis later.nil, ifotheris not a File::Stat object.
Examples:
stat0 = File.stat('/etc')
stat1 = File.stat('/tmp')
stat0.mtime # => 2026-10-02 07:52:03.151556038 -0500
stat1.mtime # => 2026-10-03 12:22:25.719215899 -0500
stat0 <=> stat1 # => -1
stat0 <=> stat0.dup # => 0
stat1 <=> stat0 # => 1
stat0 <=> :foo # => nil
Class File::Stat includes module Comparable, each of whose methods uses File::Stat#<=> for comparison.
# File 'file.c', line 646
static VALUE
rb_stat_cmp(VALUE self, VALUE other)
{
if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
stat_timestamp ts1 = statx_mtimespec(get_stat(self));
stat_timestamp ts2 = statx_mtimespec(get_stat(other));
if (ts1.tv_sec == ts2.tv_sec) {
if (ts1.tv_nsec == ts2.tv_nsec) return INT2FIX(0);
if (ts1.tv_nsec < ts2.tv_nsec) return INT2FIX(-1);
return INT2FIX(1);
}
if (ts1.tv_sec < ts2.tv_sec) return INT2FIX(-1);
return INT2FIX(1);
}
return Qnil;
}
#atime ⇒ Time
Returns a new ::Time object containing the access time
of thesnapshot in self.
See File System Timestamps.
Access time for a file is established when it is created, and may be updated when the file content is read:
filepath = '/tmp/t.tmp'
File.write(filepath, 'foo')
File.atime(filepath) # => 2026-10-04 14:58:58.805179514 -0500
stat0 = File.stat(filepath) # Take snapshot.
stat0.atime # => 2026-10-04 14:58:58.805179514 -0500
File.read(filepath) # Updates file atime.
File.atime(filepath) # => 2026-10-04 14:59:47.707059351 -0500
stat0.atime # => 2026-10-04 14:58:58.805179514 -0500 # Not updated.
stat1 = File.stat(filepath) # Take new snapshot.
stat1.atime # => 2026-10-04 14:59:47.707059351 -0500 # Updated.
File.delete(filepath) # Clean up.
Access time for a directory is established when it is created, and may be updated when its entries are read:
dirpath = '/tmp/dir'
Dir.mkdir(dirpath)
File.atime(dirpath) # => 2026-10-04 15:07:26.194548647 -0500
stat0 = File.stat(dirpath) # Take snapshot.
stat0.atime # => 2026-10-04 15:07:26.194548647 -0500
Dir.entries(dirpath) # Updates directory atime.
File.atime(dirpath) # => 2026-10-04 15:08:14.818746815 -0500 # Updated.
stat0.atime # => 2026-10-04 15:07:26.194548647 -0500 # Not updated.
stat1 = File.stat(dirpath) # Take new shapshot.
stat1.atime # => 2026-10-04 15:08:14.818746815 -0500 # Updated.
Dir.rmdir(dirpath) # Clean up.
# File 'file.c', line 1210
static VALUE
rb_stat_atime(VALUE self)
{
return stat_time(statx_atimespec(get_stat(self)));
}
#birthtime ⇒ Time
Returns a new ::Time object containing the creation time
of thesnapshot in self.
See File System Timestamps:
filepath = '/tmp/t.tmp'
File.write(filepath, 'foo')
stat = File.stat(filepath) # Take shapshot.
stat.birthtime # => 2026-10-05 13:14:24.844158315 -0500
File.delete(filepath)
stat.birthtime # => 2026-10-05 13:14:24.844158315 -0500 # Unchanged.
# File 'file.c', line 1310
static VALUE
rb_stat_birthtime(VALUE self)
{
return statx_birthtime(get_stat(self));
}
#blksize ⇒ Integer?
# File 'file.c', line 1013
static VALUE
rb_stat_blksize(VALUE self)
{
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
return ULONG2NUM(get_stat(self)->ST_(blksize));
#else
return Qnil;
#endif
}
#blocks ⇒ Integer?
# File 'file.c', line 1040
static VALUE
rb_stat_blocks(VALUE self)
{
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
# if SIZEOF_STRUCT_STAT_ST_BLOCKS > SIZEOF_LONG
return ULL2NUM(get_stat(self)->ST_(blocks));
# else
return ULONG2NUM(get_stat(self)->ST_(blocks));
# endif
#else
return Qnil;
#endif
}
#ctime ⇒ Time
On Windows, returns the birthtime for the entry in self.
On other systems, returns the time of the most recent metadata change
to thesnapshot in self:
dirpath = '/tmp/dir'
Dir.mkdir(dirpath) # Establishes directory ctime.
dirstat0 = File.stat(dirpath) # Take directory snapshot.
dirstat0.ctime # => 2026-10-06 11:07:18.073533542 -0500 # Initial directory ctime.
filepath = File.join(dirpath, 't.tmp') # => "/tmp/dir/t.tmp"
File.write(filepath, 'foo') # Establishes file ctime; upates directory ctime.
filestat0 = File.stat(filepath) # Take file snapshot.
filestat0.ctime # => 2026-10-06 11:07:52.555027213 -0500 # Initial file ctime.
dirstat0.ctime # => 2026-10-06 11:07:18.073533542 -0500 # Directory snapshot unchanged.
dirstat1 = File.stat(dirpath) # Take new directory snapshot.
dirstat1.ctime # => 2026-10-06 11:07:52.555027213 -0500 # Snapshot has new ctime.
File.write(filepath, 'bar') # Updates file ctime.
filestat0.ctime # => 2026-10-06 11:07:52.555027213 -0500 # Old file snapshot unchanged.
filestat1 = File.stat(filepath) # Take new file snapshot.
filestat1.ctime # => 2026-10-06 11:14:11.19290223 -0500 # Snapshot shows change.
FileUtils.rmtree(dirpath) # Clean up.
# File 'file.c', line 1282
static VALUE
rb_stat_ctime(VALUE self)
{
return stat_time(statx_ctimespec(get_stat(self)));
}
#dev ⇒ Integer
# File 'file.c', line 690
static VALUE
rb_stat_dev(VALUE self)
{
#if RUBY_USE_STATX
unsigned int m = get_stat(self)->stx_dev_major;
unsigned int n = get_stat(self)->stx_dev_minor;
return ULL2NUM(makedev(m, n));
#elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_DEV_T
return DEVT2NUM(get_stat(self)->st_dev);
#elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_LONG
return ULONG2NUM(get_stat(self)->st_dev);
#else
return ULL2NUM(get_stat(self)->st_dev);
#endif
}
#dev_major ⇒ Integer
# File 'file.c', line 724
static VALUE
rb_stat_dev_major(VALUE self)
{
#if RUBY_USE_STATX
return UINT2NUM(get_stat(self)->stx_dev_major);
#elif defined(major)
return UINT2NUM(major(get_stat(self)->st_dev));
#else
return Qnil;
#endif
}
#dev_minor ⇒ Integer
# File 'file.c', line 754
static VALUE
rb_stat_dev_minor(VALUE self)
{
#if RUBY_USE_STATX
return UINT2NUM(get_stat(self)->stx_dev_minor);
#elif defined(minor)
return UINT2NUM(minor(get_stat(self)->st_dev));
#else
return Qnil;
#endif
}
#ftype ⇒ String
Returns the string type of the path in thesnapshot
in self, one of:
'file'.'directory'.'characterSpecial'.'blockSpecial'.'fifo'.'link'.'socket'.
Examples:
File.stat('/etc/passwd').ftype # => "file"
File.stat('/etc').ftype # => "directory"
File.stat('/dev/null').ftype # => "characterSpecial"
File.stat('/dev/loop0').ftype # => "blockSpecial"
File.mkfifo('/tmp/pipe', 0o666)
File.stat('/tmp/pipe').ftype # => "fifo"
File.symlink('/etc/passwd', '/tmp/link')
File.lstat('/tmp/link').ftype # => "link"
require 'socket'
UNIXServer.new('/tmp/socket')
File.stat('/tmp/socket').ftype # => "socket"
File.delete('/tmp/link', '/tmp/pipe', '/tmp/socket') # Clean up.
Returns 'unknown' if the type cannot be determined.
# File 'file.c', line 7071
static VALUE
rb_stat_ftype(VALUE obj)
{
return rb_file_ftype(get_stat(obj)->ST_(mode));
}
#gid ⇒ Integer
# File 'file.c', line 880
static VALUE
rb_stat_gid(VALUE self)
{
return GIDT2NUM(get_stat(self)->ST_(gid));
}
#initialize_copy(orig)
# File 'file.c', line 7015
static VALUE
rb_stat_init_copy(VALUE copy, VALUE orig)
{
if (!OBJ_INIT_COPY(copy, orig)) return copy;
struct rb_stat *orig_rb_st;
TypedData_Get_Struct(orig, struct rb_stat, &stat_data_type, orig_rb_st);
struct rb_stat *copy_rb_st;
TypedData_Get_Struct(copy, struct rb_stat, &stat_data_type, copy_rb_st);
*copy_rb_st = *orig_rb_st;
return copy;
}
#ino ⇒ Integer
# File 'file.c', line 782
static VALUE
rb_stat_ino(VALUE self)
{
rb_io_stat_data *ptr = get_stat(self);
#ifdef HAVE_STRUCT_STAT_ST_INOHIGH
/* assume INTEGER_PACK_LSWORD_FIRST and st_inohigh is just next of st_ino */
return rb_integer_unpack(&ptr->st_ino, 2,
SIZEOF_STRUCT_STAT_ST_INO, 0,
INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER|
INTEGER_PACK_2COMP);
#else
return UIANY2NUM(ptr->ST_(ino));
#endif
}
#inspect ⇒ String
Returns a string representation of the entry
in thesnapshot in self;
here we split the returned strings for readability:
puts File.stat('/etc/passwd').inspect.split(', ')
Output:
{#<}File::Stat dev=0x10302
ino=11141413
mode=0100644
nlink=1
uid=0
gid=0
rdev=0x0
size=3092
blksize=4096
blocks=8
atime=2026-10-07 11:25:01.307589569 -0500
mtime=2025-06-10 11:10:47.358999941 -0500
ctime=2025-06-10 11:10:47.38899994 -0500>
puts File.stat('/etc').inspect.split(', ')
Output:
{#<}File::Stat dev=0x10302
ino=11141121
mode=040755
nlink=143
uid=0
gid=0
rdev=0x0
size=12288
blksize=4096
blocks=24
atime=2026-10-04 14:29:46.907775905 -0500
mtime=2026-10-07 06:13:35.116915307 -0500
ctime=2026-10-07 06:13:35.116915307 -0500>
# File 'file.c', line 1375
static VALUE
rb_stat_inspect(VALUE self)
{
VALUE str;
size_t i;
static const struct {
const char *name;
VALUE (*func)(VALUE);
} member[] = {
{"dev", rb_stat_dev},
{"ino", rb_stat_ino},
{"mode", rb_stat_mode},
{"nlink", rb_stat_nlink},
{"uid", rb_stat_uid},
{"gid", rb_stat_gid},
{"rdev", rb_stat_rdev},
{"size", rb_stat_size},
{"blksize", rb_stat_blksize},
{"blocks", rb_stat_blocks},
{"atime", rb_stat_atime},
{"mtime", rb_stat_mtime},
{"ctime", rb_stat_ctime},
#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
{"birthtime", rb_stat_birthtime},
#endif
};
struct rb_stat* rb_st;
TypedData_Get_Struct(self, struct rb_stat, &stat_data_type, rb_st);
if (!rb_st->initialized) {
return rb_sprintf("#<%s: uninitialized>", rb_obj_classname(self));
}
str = rb_str_buf_new2("#<");
rb_str_buf_cat2(str, rb_obj_classname(self));
rb_str_buf_cat2(str, " ");
for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
VALUE v;
if (i > 0) {
rb_str_buf_cat2(str, ", ");
}
rb_str_buf_cat2(str, member[i].name);
rb_str_buf_cat2(str, "=");
v = (*member[i].func)(self);
if (i == 2) { /* mode */
rb_str_catf(str, "0%lo", (unsigned long)NUM2ULONG(v));
}
else if (i == 0 || i == 6) { /* dev/rdev */
rb_str_catf(str, "0x%"PRI_DEVT_PREFIX"x", NUM2DEVT(v));
}
else {
rb_str_append(str, rb_inspect(v));
}
}
rb_str_buf_cat2(str, ">");
return str;
}
#mode ⇒ Integer
# File 'file.c', line 816
static VALUE
rb_stat_mode(VALUE self)
{
return UINT2NUM(ST2UINT(get_stat(self)->ST_(mode)));
}
#mtime ⇒ Time
Returns a new ::Time object containing the modification time
of the object represented by self
at the time self was created;
seeSnapshot:
path = 't.tmp'
file = File.new(path, 'w+')
stat = File.stat(path)
stat.mtime # => 2026-09-19 08:49:08.846933858 -0500
file.write('foo')
file.flush
File.mtime(path) # => 2026-09-19 08:50:14.381365572 -0500
stat.mtime # => 2026-09-19 08:49:08.846933858 -0500
stat = File.stat(path)
stat.mtime # => 2026-09-19 08:50:14.381365572 -0500
File.unlink(path) # Clean up.
# File 'file.c', line 1243
static VALUE
rb_stat_mtime(VALUE self)
{
return stat_time(statx_mtimespec(get_stat(self)));
}
#nlink ⇒ Integer
# File 'file.c', line 834
static VALUE
rb_stat_nlink(VALUE self)
{
/* struct stat::st_nlink is nlink_t in POSIX. Not the case for Windows. */
const rb_io_stat_data *ptr = get_stat(self);
return UIANY2NUM(ptr->ST_(nlink));
}
#rdev ⇒ Integer?
# File 'file.c', line 898
static VALUE
rb_stat_rdev(VALUE self)
{
#if RUBY_USE_STATX
unsigned int m = get_stat(self)->stx_rdev_major;
unsigned int n = get_stat(self)->stx_rdev_minor;
return ULL2NUM(makedev(m, n));
#elif !defined(HAVE_STRUCT_STAT_ST_RDEV)
return Qnil;
#elif SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_DEV_T
return DEVT2NUM(get_stat(self)->ST_(rdev));
#elif SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_LONG
return ULONG2NUM(get_stat(self)->ST_(rdev));
#else
return ULL2NUM(get_stat(self)->ST_(rdev));
#endif
}
#rdev_major ⇒ Integer
# File 'file.c', line 926
static VALUE
rb_stat_rdev_major(VALUE self)
{
#if RUBY_USE_STATX
return UINT2NUM(get_stat(self)->stx_rdev_major);
#elif defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(major)
return UINT2NUM(major(get_stat(self)->ST_(rdev)));
#else
return Qnil;
#endif
}
#rdev_minor ⇒ Integer
# File 'file.c', line 948
static VALUE
rb_stat_rdev_minor(VALUE self)
{
#if RUBY_USE_STATX
return UINT2NUM(get_stat(self)->stx_rdev_minor);
#elif defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(minor)
return UINT2NUM(minor(get_stat(self)->ST_(rdev)));
#else
return Qnil;
#endif
}
#uid ⇒ Integer
[ GitHub ]# File 'file.c', line 853
static VALUE
rb_stat_uid(VALUE self)
{
return UIDT2NUM(get_stat(self)->ST_(uid));
}