Namespace for file utility methods for copying, moving, removing, etc.
What’s Here
First, what’s elsewhere. Module FileUtils:
-
Inherits from class Object.
-
Supplements class File (but is not included or extended there).
Here, module FileUtils provides methods that are useful for:
Creating
-
::mkdir
: Creates directories. -
::mkdir_p
,::makedirs
,::mkpath
: Creates directories, also creating ancestor directories as needed. -
::link_entry
: Creates a hard link. -
::ln_sf
: Creates symbolic links, overwriting if necessary. -
::ln_sr
: Creates symbolic links relative to targets
Deleting
-
::remove_dir
: Removes a directory and its descendants. -
::remove_entry
: Removes an entry, including its descendants if it is a directory. -
::remove_entry_secure
: Like::remove_entry
, but removes securely. -
::remove_file
: Removes a file entry. -
::rm_f
,::safe_unlink
: Like::rm
, but removes forcibly. -
::rm_r
: Removes entries and their descendants. -
::rmdir
: Removes directories.
Querying
-
::uptodate?
: Returns whether a given entry is newer than given other entries.
Setting
-
::chmod
: Sets permissions for an entry. -
::chmod_R
: Sets permissions for an entry and its descendants. -
::chown
: Sets the owner and group for entries. -
::chown_R
: Sets the owner and group for entries and their descendants. -
::touch
: Sets modification and access times for entries, creating if necessary.
Comparing
-
::compare_file
,::cmp
,::identical?
: Returns whether two entries are identical. -
::compare_stream
: Returns whether two streams are identical.
Copying
-
::copy_entry
: Recursively copies an entry. -
::copy_file
: Copies an entry. -
::copy_stream
: Copies a stream. -
::cp_lr
: Recursively creates hard links. -
::cp_r
: Recursively copies files, retaining mode, owner, and group. -
::install
: Recursively copies files, optionally setting mode, owner, and group.
Moving
Options
-
::collect_method
: Returns the names of methods that accept a given option. -
::commands
: Returns the names of methods that accept options. -
::have_option?
: Returns whether a given method accepts a given option. -
::options
: Returns all option names. -
::options_of
: Returns the names of the options for a given method.
Path Arguments
Some methods in FileUtils accept path arguments, which are interpreted as paths to filesystem entries:
-
If the argument is a string, that value is the path.
-
If the argument has method
:to_path
, it is converted via that method. -
If the argument has method
:to_str
, it is converted via that method.
About the Examples
Some examples here involve trees of file entries. For these, we sometimes display trees using the tree command-line utility, which is a recursive directory-listing utility that produces a depth-indented listing of files and directories.
We use a helper method to launch the command and control the format:
def tree(dirpath = '.') command = "tree --noreport --charset=ascii #{dirpath}" system(command) end
To illustrate:
tree('src0') # => src0 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt
Avoiding the TOCTTOU Vulnerability
For certain methods that recursively remove entries, there is a potential vulnerability called the Time-of-check to time-of-use, or TOCTTOU, vulnerability that can exist when:
-
An ancestor directory of the entry at the target path is world writable; such directories include
/tmp
. -
The directory tree at the target path includes:
-
A world-writable descendant directory.
-
A symbolic link.
-
To avoid that vulnerability, you can use this method to remove entries:
-
FileUtils.remove_entry_secure
: removes recursively if the target path points to a directory.
Also available are these methods, each of which calls FileUtils.remove_entry_secure:
-
FileUtils.rm_r
with keyword argumentsecure: true
. -
FileUtils.rm_rf
with keyword argumentsecure: true
.
Finally, this method for moving entries calls FileUtils.remove_entry_secure if the source and destination are on different file systems (which means that the “move” is really a copy and remove):
-
FileUtils.mv
with keyword argumentsecure: true
.
Method FileUtils.remove_entry_secure removes securely by applying a special pre-process:
-
If the target path points to a directory, this method uses methods
File#chown
andFile#chmod
in removing directories. -
The owner of the target directory should be either the current process or the super user (root).
WARNING: You must ensure that ALL parent directories cannot be moved by other untrusted users. For example, parent directories should not be owned by untrusted users, and should not be world writable except when the sticky bit is set.
For details of this security vulnerability, see Perl cases:
This hash table holds command options.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 238
def cd(dir, verbose: nil, &block) # :yield: dir
fu_output_message "cd #{dir}" if verbose
result = Dir.chdir(dir, &block)
fu_output_message 'cd -' if verbose and block
result
end
Changes the working directory to the given dir
, which should be interpretable as a path:
With no block given, changes the current directory to the directory at dir
; returns zero:
FileUtils.pwd # => "/rdoc/fileutils" FileUtils.cd('..') FileUtils.pwd # => "/rdoc" FileUtils.cd('fileutils')
With a block given, changes the current directory to the directory at dir
, calls the block with argument dir
, and restores the original current directory; returns the block’s value:
FileUtils.pwd # => "/rdoc/fileutils" FileUtils.cd('..') { |arg| [arg, FileUtils.pwd] } # => ["..", "/rdoc"] FileUtils.pwd # => "/rdoc/fileutils"
Keyword arguments:
-
verbose: true
- prints an equivalent command:FileUtils.cd('..') FileUtils.cd('fileutils')
Output:
cd .. cd fileutils
Related: FileUtils.pwd
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1803
def chmod(mode, list, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
return if noop
list.each do |path|
Entry_.new(path).chmod(fu_mode(mode, path))
end
end
Changes permissions on the entries at the paths given in list
(a single path or an array of paths) to the permissions given by mode
; returns list
if it is an array, [list]
otherwise:
-
Modifies each entry that is a regular file using
File.chmod
. -
Modifies each entry that is a symbolic link using
File.lchmod
.
Argument list
or its elements should be interpretable as paths.
Argument mode
may be either an integer or a string:
-
Integer
mode
: represents the permission bits to be set:FileUtils.chmod(0755, 'src0.txt') FileUtils.chmod(0644, ['src0.txt', 'src0.dat'])
-
String
mode
: represents the permissions to be set:The string is of the form
[targets][[operator][perms[,perms]]
, where:-
targets
may be any combination of these letters:-
'u'
: permissions apply to the file’s owner. -
'g'
: permissions apply to users in the file’s group. -
'o'
: permissions apply to other users not in the file’s group. -
'a'
(the default): permissions apply to all users.
-
-
operator
may be one of these letters:-
'+'
: adds permissions. -
'-'
: removes permissions. -
'='
: sets (replaces) permissions.
-
-
perms
(may be repeated, with separating commas) may be any combination of these letters:-
'r'
: Read. -
'w'
: Write. -
'x'
: Execute (search, for a directory). -
'X'
: Search (for a directories only; must be used with'+'
) -
's'
: Uid or gid. -
't'
: Sticky bit.
-
Examples:
FileUtils.chmod('u=wrx,go=rx', 'src1.txt') FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby')
-
Keyword arguments:
-
noop: true
- does not change permissions; returnsnil
. -
verbose: true
- prints an equivalent command:FileUtils.chmod(0755, 'src0.txt', noop: true, verbose: true) FileUtils.chmod(0644, ['src0.txt', 'src0.dat'], noop: true, verbose: true) FileUtils.chmod('u=wrx,go=rx', 'src1.txt', noop: true, verbose: true) FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby', noop: true, verbose: true)
Output:
chmod 755 src0.txt chmod 644 src0.txt src0.dat chmod u=wrx,go=rx src1.txt chmod u=wrx,go=rx /usr/bin/ruby
Related: FileUtils.chmod_R
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1815
def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
fu_output_message sprintf('chmod -R%s %s %s',
(force ? 'f' : ''),
mode_to_s(mode), list.join(' ')) if verbose
return if noop
list.each do |root|
Entry_.new(root).traverse do |ent|
begin
ent.chmod(fu_mode(mode, ent.path))
rescue
raise unless force
end
end
end
end
Like FileUtils.chmod
, but changes permissions recursively.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1896
def chown(user, group, list, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message sprintf('chown %s %s',
(group ? "#{user}:#{group}" : user || ':'),
list.join(' ')) if verbose
return if noop
uid = fu_get_uid(user)
gid = fu_get_gid(group)
list.each do |path|
Entry_.new(path).chown uid, gid
end
end
Changes the owner and group on the entries at the paths given in list
(a single path or an array of paths) to the given user
and group
; returns list
if it is an array, [list]
otherwise:
-
Modifies each entry that is a regular file using
File.chown
. -
Modifies each entry that is a symbolic link using
File.lchown
.
Argument list
or its elements should be interpretable as paths.
User and group:
-
Argument
user
may be a user name or a user id; ifnil
or-1
, the user is not changed. -
Argument
group
may be a group name or a group id; ifnil
or-1
, the group is not changed. -
The user must be a member of the group.
Examples:
# One path. # User and group as string names. File.stat('src0.txt').uid # => 1004 File.stat('src0.txt').gid # => 1004 FileUtils.chown('user2', 'group1', 'src0.txt') File.stat('src0.txt').uid # => 1006 File.stat('src0.txt').gid # => 1005 # User and group as uid and gid. FileUtils.chown(1004, 1004, 'src0.txt') File.stat('src0.txt').uid # => 1004 File.stat('src0.txt').gid # => 1004 # Array of paths. FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat']) # Directory (not recursive). FileUtils.chown('user2', 'group1', '.')
Keyword arguments:
-
noop: true
- does not change permissions; returnsnil
. -
verbose: true
- prints an equivalent command:FileUtils.chown('user2', 'group1', 'src0.txt', noop: true, verbose: true) FileUtils.chown(1004, 1004, 'src0.txt', noop: true, verbose: true) FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'], noop: true, verbose: true) FileUtils.chown('user2', 'group1', path, noop: true, verbose: true) FileUtils.chown('user2', 'group1', '.', noop: true, verbose: true)
Output:
chown user2:group1 src0.txt chown 1004:1004 src0.txt chown 1006:1005 src0.txt src0.dat chown user2:group1 src0.txt chown user2:group1 .
Related: FileUtils.chown_R
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1912
def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
fu_output_message sprintf('chown -R%s %s %s',
(force ? 'f' : ''),
(group ? "#{user}:#{group}" : user || ':'),
list.join(' ')) if verbose
return if noop
uid = fu_get_uid(user)
gid = fu_get_gid(group)
list.each do |root|
Entry_.new(root).traverse do |ent|
begin
ent.chown uid, gid
rescue
raise unless force
end
end
end
end
Like FileUtils.chown
, but changes owner and group recursively.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 2609
def self.collect_method(opt)
OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
end
Returns an array of the string method names of the methods that accept the given keyword option opt
; the argument must be a symbol:
FileUtils.collect_method(:preserve) # => ["cp", "copy", "cp_r", "install"]
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 2570
def self.commands
OPT_TABLE.keys
end
Returns an array of the string names of FileUtils methods that accept one or more keyword arguments:
FileUtils.commands.sort.take(3) # => ["cd", "chdir", "chmod"]
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1507
def compare_file(a, b)
return false unless File.size(a) == File.size(b)
File.open(a, 'rb') {|fa|
File.open(b, 'rb') {|fb|
return compare_stream(fa, fb)
}
}
end
Returns true
if the contents of files a
and b
are identical, false
otherwise.
Arguments a
and b
should be interpretable as a path.
FileUtils.identical?
and FileUtils.cmp
are aliases for FileUtils.compare_file
.
Related: FileUtils.compare_stream
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1530
def compare_stream(a, b)
bsize = fu_stream_blksize(a, b)
sa = String.new(capacity: bsize)
sb = String.new(capacity: bsize)
begin
a.read(bsize, sa)
b.read(bsize, sb)
return true if sa.empty? && sb.empty?
end while sa == sb
false
end
Returns true
if the contents of streams a
and b
are identical, false
otherwise.
Arguments a
and b
should be interpretable as a path.
Related: FileUtils.compare_file
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1040
def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
if dereference_root
src = File.realpath(src)
end
Entry_.new(src, nil, false).wrap_traverse(proc do |ent|
destent = Entry_.new(dest, ent.rel, false)
File.unlink destent.path if remove_destination && (File.file?(destent.path) || File.symlink?(destent.path))
ent.copy destent.path
end, proc do |ent|
destent = Entry_.new(dest, ent.rel, false)
ent.copy_metadata destent.path if preserve
end)
end
Recursively copies files from src
to dest
.
Arguments src
and dest
should be interpretable as paths.
If src
is the path to a file, copies src
to dest
:
FileUtils.touch('src0.txt') File.exist?('dest0.txt') # => false FileUtils.copy_entry('src0.txt', 'dest0.txt') File.file?('dest0.txt') # => true
If src
is a directory, recursively copies src
to dest
:
tree('src1') # => src1 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt FileUtils.copy_entry('src1', 'dest1') tree('dest1') # => dest1 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt
The recursive copying preserves file types for regular files, directories, and symbolic links; other file types (FIFO streams, device files, etc.) are not supported.
Keyword arguments:
-
dereference_root: true
- ifsrc
is a symbolic link, follows the link. -
preserve: true
- preserves file times. -
remove_destination: true
- removesdest
before copying files.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1076
def copy_file(src, dest, preserve = false, dereference = true)
ent = Entry_.new(src, nil, dereference)
ent.copy_file dest
ent.copy_metadata dest if preserve
end
Copies file from src
to dest
, which should not be directories.
Arguments src
and dest
should be interpretable as paths.
Examples:
FileUtils.touch('src0.txt') FileUtils.copy_file('src0.txt', 'dest0.txt') File.file?('dest0.txt') # => true
Keyword arguments:
-
dereference: false
- ifsrc
is a symbolic link, does not follow the link. -
preserve: true
- preserves file times. -
remove_destination: true
- removesdest
before copying files.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1088
def copy_stream(src, dest)
IO.copy_stream(src, dest)
end
Copies IO stream src
to IO stream dest
via IO.copy_stream
.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 873
def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
copy_file s, d, preserve
end
end
Copies files.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
If src
is the path to a file and dest
is not the path to a directory, copies src
to dest
:
FileUtils.touch('src0.txt') File.exist?('dest0.txt') # => false FileUtils.cp('src0.txt', 'dest0.txt') File.file?('dest0.txt') # => true
If src
is the path to a file and dest
is the path to a directory, copies src
to dest/src
:
FileUtils.touch('src1.txt') FileUtils.mkdir('dest1') FileUtils.cp('src1.txt', 'dest1') File.file?('dest1/src1.txt') # => true
If src
is an array of paths to files and dest
is the path to a directory, copies from each src
to dest
:
src_file_paths = ['src2.txt', 'src2.dat'] FileUtils.touch(src_file_paths) FileUtils.mkdir('dest2') FileUtils.cp(src_file_paths, 'dest2') File.file?('dest2/src2.txt') # => true File.file?('dest2/src2.dat') # => true
Keyword arguments:
-
preserve: true
- preserves file times. -
noop: true
- does not copy files. -
verbose: true
- prints an equivalent command:FileUtils.cp('src0.txt', 'dest0.txt', noop: true, verbose: true) FileUtils.cp('src1.txt', 'dest1', noop: true, verbose: true) FileUtils.cp(src_file_paths, 'dest2', noop: true, verbose: true)
Output:
cp src0.txt dest0.txt cp src1.txt dest1 cp src2.txt src2.dat dest2
Raises an exception if src
is a directory.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 627
def cp_lr(src, dest, noop: nil, verbose: nil,
dereference_root: true, remove_destination: false)
fu_output_message "cp -lr#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
link_entry s, d, dereference_root, remove_destination
end
end
Creates hard links.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
If src
is the path to a directory and dest
does not exist, creates links dest
and descendents pointing to src
and its descendents:
tree('src0') # => src0 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt File.exist?('dest0') # => false FileUtils.cp_lr('src0', 'dest0') tree('dest0') # => dest0 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt
If src
and dest
are both paths to directories, creates links dest/src
and descendents pointing to src
and its descendents:
tree('src1') # => src1 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt FileUtils.mkdir('dest1') FileUtils.cp_lr('src1', 'dest1') tree('dest1') # => dest1 # `-- src1 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt
If src
is an array of paths to entries and dest
is the path to a directory, for each path filepath
in src
, creates a link at dest/filepath
pointing to that path:
tree('src2') # => src2 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt FileUtils.mkdir('dest2') FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2') tree('dest2') # => dest2 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt
Keyword arguments:
-
dereference_root: false
- ifsrc
is a symbolic link, does not dereference it. -
noop: true
- does not create links. -
remove_destination: true
- removesdest
before creating links. -
verbose: true
- prints an equivalent command:FileUtils.cp_lr('src0', 'dest0', noop: true, verbose: true) FileUtils.cp_lr('src1', 'dest1', noop: true, verbose: true) FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2', noop: true, verbose: true)
Output:
cp -lr src0 dest0 cp -lr src1 dest1 cp -lr src2/sub0 src2/sub1 dest2
Raises an exception if dest
is the path to an existing file or directory and keyword argument remove_destination: true
is not given.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 985
def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
dereference_root: true, remove_destination: nil)
fu_output_message "cp -r#{preserve ? 'p' : ''}#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
copy_entry s, d, preserve, dereference_root, remove_destination
end
end
Recursively copies files.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
The mode, owner, and group are retained in the copy; to change those, use FileUtils.install
instead.
If src
is the path to a file and dest
is not the path to a directory, copies src
to dest
:
FileUtils.touch('src0.txt') File.exist?('dest0.txt') # => false FileUtils.cp_r('src0.txt', 'dest0.txt') File.file?('dest0.txt') # => true
If src
is the path to a file and dest
is the path to a directory, copies src
to dest/src
:
FileUtils.touch('src1.txt') FileUtils.mkdir('dest1') FileUtils.cp_r('src1.txt', 'dest1') File.file?('dest1/src1.txt') # => true
If src
is the path to a directory and dest
does not exist, recursively copies src
to dest
:
tree('src2') # => src2 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt FileUtils.exist?('dest2') # => false FileUtils.cp_r('src2', 'dest2') tree('dest2') # => dest2 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt
If src
and dest
are paths to directories, recursively copies src
to dest/src
:
tree('src3') # => src3 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt FileUtils.mkdir('dest3') FileUtils.cp_r('src3', 'dest3') tree('dest3') # => dest3 # `-- src3 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt
If src
is an array of paths and dest
is a directory, recursively copies from each path in src
to dest
; the paths in src
may point to files and/or directories.
Keyword arguments:
-
dereference_root: false
- ifsrc
is a symbolic link, does not dereference it. -
noop: true
- does not copy files. -
preserve: true
- preserves file times. -
remove_destination: true
- removesdest
before copying files. -
verbose: true
- prints an equivalent command:FileUtils.cp_r('src0.txt', 'dest0.txt', noop: true, verbose: true) FileUtils.cp_r('src1.txt', 'dest1', noop: true, verbose: true) FileUtils.cp_r('src2', 'dest2', noop: true, verbose: true) FileUtils.cp_r('src3', 'dest3', noop: true, verbose: true)
Output:
cp -r src0.txt dest0.txt cp -r src1.txt dest1 cp -r src2 dest2 cp -r src3 dest3
Raises an exception of src
is the path to a directory and dest
is the path to a file.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 2588
def self.have_option?(mid, opt)
li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
li.include?(opt)
end
Returns true
if method mid
accepts the given option opt
, false
otherwise; the arguments may be strings or symbols:
FileUtils.have_option?(:chmod, :noop) # => true FileUtils.have_option?('chmod', 'secure') # => false
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1609
def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
noop: nil, verbose: nil)
if verbose
msg = +"install -c"
msg << ' -p' if preserve
msg << ' -m ' << mode_to_s(mode) if mode
msg << " -o #{owner}" if owner
msg << " -g #{group}" if group
msg << ' ' << [src,dest].flatten.join(' ')
fu_output_message msg
end
return if noop
uid = fu_get_uid(owner)
gid = fu_get_gid(group)
fu_each_src_dest(src, dest) do |s, d|
st = File.stat(s)
unless File.exist?(d) and compare_file(s, d)
remove_file d, true
if d.end_with?('/')
mkdir_p d
copy_file s, d + File.basename(s)
else
mkdir_p File.expand_path('..', d)
copy_file s, d
end
File.utime st.atime, st.mtime, d if preserve
File.chmod fu_mode(mode, st), d if mode
File.chown uid, gid, d if uid or gid
end
end
end
Copies a file entry. See install(1).
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths;
If the entry at dest
does not exist, copies from src
to dest
:
File.read('src0.txt') # => "aaa\n" File.exist?('dest0.txt') # => false FileUtils.install('src0.txt', 'dest0.txt') File.read('dest0.txt') # => "aaa\n"
If dest
is a file entry, copies from src
to dest
, overwriting:
File.read('src1.txt') # => "aaa\n" File.read('dest1.txt') # => "bbb\n" FileUtils.install('src1.txt', 'dest1.txt') File.read('dest1.txt') # => "aaa\n"
If dest
is a directory entry, copies from src
to dest/src
, overwriting if necessary:
File.read('src2.txt') # => "aaa\n" File.read('dest2/src2.txt') # => "bbb\n" FileUtils.install('src2.txt', 'dest2') File.read('dest2/src2.txt') # => "aaa\n"
If src
is an array of paths and dest
points to a directory, copies each path path
in src
to dest/path
:
File.file?('src3.txt') # => true File.file?('src3.dat') # => true FileUtils.mkdir('dest3') FileUtils.install(['src3.txt', 'src3.dat'], 'dest3') File.file?('dest3/src3.txt') # => true File.file?('dest3/src3.dat') # => true
Keyword arguments:
-
group: group
- changes the group if notnil
, usingFile.chown
. -
mode: permissions
- changes the permissions. usingFile.chmod
. -
noop: true
- does not copy entries; returnsnil
. -
owner: owner
- changes the owner if notnil
, usingFile.chown
. -
preserve: true
- preserve timestamps usingFile.utime
. -
verbose: true
- prints an equivalent command:FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true) FileUtils.install('src1.txt', 'dest1.txt', noop: true, verbose: true) FileUtils.install('src2.txt', 'dest2', noop: true, verbose: true)
Output:
install -c src0.txt dest0.txt install -c src1.txt dest1.txt install -c src2.txt dest2
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 812
def link_entry(src, dest, dereference_root = false, remove_destination = false)
Entry_.new(src, nil, dereference_root).traverse do |ent|
destent = Entry_.new(dest, ent.rel, false)
File.unlink destent.path if remove_destination && File.file?(destent.path)
ent.link destent.path
end
end
Creates hard links; returns nil
.
Arguments src
and dest
should be interpretable as paths.
If src
is the path to a file and dest
does not exist, creates a hard link at dest
pointing to src
:
FileUtils.touch('src0.txt') File.exist?('dest0.txt') # => false FileUtils.link_entry('src0.txt', 'dest0.txt') File.file?('dest0.txt') # => true
If src
is the path to a directory and dest
does not exist, recursively creates hard links at dest
pointing to paths in src
:
FileUtils.mkdir_p(['src1/dir0', 'src1/dir1']) src_file_paths = [ 'src1/dir0/t0.txt', 'src1/dir0/t1.txt', 'src1/dir1/t2.txt', 'src1/dir1/t3.txt', ] FileUtils.touch(src_file_paths) File.directory?('dest1') # => true FileUtils.link_entry('src1', 'dest1') File.file?('dest1/dir0/t0.txt') # => true File.file?('dest1/dir0/t1.txt') # => true File.file?('dest1/dir1/t2.txt') # => true File.file?('dest1/dir1/t3.txt') # => true
Keyword arguments:
-
dereference_root: true
- dereferencessrc
if it is a symbolic link. -
remove_destination: true
- removesdest
before creating links.
Raises an exception if dest
is the path to an existing file or directory and keyword argument remove_destination: true
is not given.
Related: FileUtils.ln
(has different options).
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 516
def ln(src, dest, force: nil, noop: nil, verbose: nil)
fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
remove_file d, true if force
File.link s, d
end
end
Creates hard links.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
When src
is the path to an existing file and dest
is the path to a non-existent file, creates a hard link at dest
pointing to src
; returns zero:
Dir.children('tmp0/') # => ["t.txt"] Dir.children('tmp1/') # => [] FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk') # => 0 Dir.children('tmp1/') # => ["t.lnk"]
When src
is the path to an existing file and dest
is the path to an existing directory, creates a hard link at dest/src
pointing to src
; returns zero:
Dir.children('tmp2') # => ["t.dat"] Dir.children('tmp3') # => [] FileUtils.ln('tmp2/t.dat', 'tmp3') # => 0 Dir.children('tmp3') # => ["t.dat"]
When src
is an array of paths to existing files and dest
is the path to an existing directory, then for each path target
in src
, creates a hard link at dest/target
pointing to target
; returns src
:
Dir.children('tmp4/') # => [] FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/') # => ["tmp0/t.txt", "tmp2/t.dat"] Dir.children('tmp4/') # => ["t.dat", "t.txt"]
Keyword arguments:
-
force: true
- overwritesdest
if it exists. -
noop: true
- does not create links. -
verbose: true
- prints an equivalent command:FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk', verbose: true) FileUtils.ln('tmp2/t.dat', 'tmp3', verbose: true) FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/', verbose: true)
Output:
ln tmp0/t.txt tmp1/t.lnk ln tmp2/t.dat tmp3 ln tmp0/t.txt tmp2/t.dat tmp4/
Raises an exception if dest
is the path to an existing file and keyword argument force
is not true
.
Related: FileUtils.link_entry
(has different options).
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 706
def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
if relative
return ln_sr(src, dest, force: force, noop: noop, verbose: verbose)
end
fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
remove_file d, true if force
File.symlink s, d
end
end
Creates symbolic links.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
If src
is the path to an existing file:
-
When
dest
is the path to a non-existent file, creates a symbolic link atdest
pointing tosrc
:FileUtils.touch('src0.txt') File.exist?('dest0.txt') # => false FileUtils.ln_s('src0.txt', 'dest0.txt') File.symlink?('dest0.txt') # => true
-
When
dest
is the path to an existing file, creates a symbolic link atdest
pointing tosrc
if and only if keyword argumentforce: true
is given (raises an exception otherwise):FileUtils.touch('src1.txt') FileUtils.touch('dest1.txt') FileUtils.ln_s('src1.txt', 'dest1.txt', force: true) FileTest.symlink?('dest1.txt') # => true FileUtils.ln_s('src1.txt', 'dest1.txt') # Raises Errno::EEXIST.
If dest
is the path to a directory, creates a symbolic link at dest/src
pointing to src
:
FileUtils.touch('src2.txt') FileUtils.mkdir('destdir2') FileUtils.ln_s('src2.txt', 'destdir2') File.symlink?('destdir2/src2.txt') # => true
If src
is an array of paths to existing files and dest
is a directory, for each child child
in src
creates a symbolic link dest/child
pointing to child
:
FileUtils.mkdir('srcdir3') FileUtils.touch('srcdir3/src0.txt') FileUtils.touch('srcdir3/src1.txt') FileUtils.mkdir('destdir3') FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3') File.symlink?('destdir3/src0.txt') # => true File.symlink?('destdir3/src1.txt') # => true
Keyword arguments:
-
force: true
- overwritesdest
if it exists. -
relative: false
- create links relative todest
. -
noop: true
- does not create links. -
verbose: true
- prints an equivalent command:FileUtils.ln_s('src0.txt', 'dest0.txt', noop: true, verbose: true) FileUtils.ln_s('src1.txt', 'destdir1', noop: true, verbose: true) FileUtils.ln_s('src2.txt', 'dest2.txt', force: true, noop: true, verbose: true) FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3', noop: true, verbose: true)
Output:
ln -s src0.txt dest0.txt ln -s src1.txt destdir1 ln -sf src2.txt dest2.txt ln -s srcdir3/src0.txt srcdir3/src1.txt destdir3
Related: FileUtils.ln_sf
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 724
def ln_sf(src, dest, noop: nil, verbose: nil)
ln_s src, dest, force: true, noop: noop, verbose: verbose
end
Like FileUtils.ln_s
, but always with keyword argument force: true
given.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 731
def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
options = "#{force ? 'f' : ''}#{target_directory ? '' : 'T'}"
dest = File.path(dest)
srcs = Array(src)
link = proc do |s, target_dir_p = true|
s = File.path(s)
if target_dir_p
d = File.join(destdirs = dest, File.basename(s))
else
destdirs = File.dirname(d = dest)
end
destdirs = fu_split_path(File.realpath(destdirs))
if fu_starting_path?(s)
srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
base = fu_relative_components_from(srcdirs, destdirs)
s = File.join(*base)
else
srcdirs = fu_clean_components(*fu_split_path(s))
base = fu_relative_components_from(fu_split_path(Dir.pwd), destdirs)
while srcdirs.first&. == ".." and base.last&.!=("..") and !fu_starting_path?(base.last)
srcdirs.shift
base.pop
end
s = File.join(*base, *srcdirs)
end
fu_output_message "ln -s#{options} #{s} #{d}" if verbose
next if noop
remove_file d, true if force
File.symlink s, d
end
case srcs.size
when 0
when 1
link[srcs[0], target_directory && File.directory?(dest)]
else
srcs.each(&link)
end
end
Like FileUtils.ln_s
, but create links relative to dest
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 316
def mkdir(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "mkdir #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
return if noop
list.each do |dir|
fu_mkdir dir, mode
end
end
Creates directories at the paths in the given list
(a single path or an array of paths); returns list
if it is an array, [list]
otherwise.
Argument list
or its elements should be interpretable as paths.
With no keyword arguments, creates a directory at each path
in list
by calling: Dir.mkdir(path, mode)
; see Dir.mkdir
:
FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"] FileUtils.mkdir('tmp4') # => ["tmp4"]
Keyword arguments:
-
mode: mode
- also callsFile.chmod(mode, path)
; seeFile.chmod
. -
noop: true
- does not create directories. -
verbose: true
- prints an equivalent command:FileUtils.mkdir(%w[tmp0 tmp1], verbose: true) FileUtils.mkdir(%w[tmp2 tmp3], mode: 0700, verbose: true)
Output:
mkdir tmp0 tmp1 mkdir -m 700 tmp2 tmp3
Raises an exception if any path points to an existing file or directory, or if for any reason a directory cannot be created.
Related: FileUtils.mkdir_p
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 365
def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
return *list if noop
list.each do |item|
path = remove_trailing_slash(item)
stack = []
until File.directory?(path) || File.dirname(path) == path
stack.push path
path = File.dirname(path)
end
stack.reverse_each do |dir|
begin
fu_mkdir dir, mode
rescue SystemCallError
raise unless File.directory?(dir)
end
end
end
return *list
end
Creates directories at the paths in the given list
(a single path or an array of paths), also creating ancestor directories as needed; returns list
if it is an array, [list]
otherwise.
Argument list
or its elements should be interpretable as paths.
With no keyword arguments, creates a directory at each path
in list
, along with any needed ancestor directories, by calling: Dir.mkdir(path, mode)
; see Dir.mkdir
:
FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"] FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"]
Keyword arguments:
-
mode: mode
- also callsFile.chmod(mode, path)
; seeFile.chmod
. -
noop: true
- does not create directories. -
verbose: true
- prints an equivalent command:FileUtils.mkdir_p(%w[tmp0 tmp1], verbose: true) FileUtils.mkdir_p(%w[tmp2 tmp3], mode: 0700, verbose: true)
Output:
mkdir -p tmp0 tmp1 mkdir -p -m 700 tmp2 tmp3
Raises an exception if for any reason a directory cannot be created.
FileUtils.mkpath
and FileUtils.makedirs
are aliases for FileUtils.mkdir_p
.
Related: FileUtils.mkdir
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1157
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
destent = Entry_.new(d, nil, true)
begin
if destent.exist?
if destent.directory?
raise Errno::EEXIST, d
end
end
begin
File.rename s, d
rescue Errno::EXDEV,
Errno::EPERM # move from unencrypted to encrypted dir (ext4)
copy_entry s, d, true
if secure
remove_entry_secure s, force
else
remove_entry s, force
end
end
rescue SystemCallError
raise unless force
end
end
end
Moves entries.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
If src
and dest
are on different file systems, first copies, then removes src
.
May cause a local vulnerability if not called with keyword argument secure: true
; see Avoiding the TOCTTOU Vulnerability.
If src
is the path to a single file or directory and dest
does not exist, moves src
to dest
:
tree('src0') # => src0 # |-- src0.txt # `-- src1.txt File.exist?('dest0') # => false FileUtils.mv('src0', 'dest0') File.exist?('src0') # => false tree('dest0') # => dest0 # |-- src0.txt # `-- src1.txt
If src
is an array of paths to files and directories and dest
is the path to a directory, copies from each path in the array to dest
:
File.file?('src1.txt') # => true tree('src1') # => src1 # |-- src.dat # `-- src.txt Dir.empty?('dest1') # => true FileUtils.mv(['src1.txt', 'src1'], 'dest1') tree('dest1') # => dest1 # |-- src1 # | |-- src.dat # | `-- src.txt # `-- src1.txt
Keyword arguments:
-
force: true
- if the move includes removingsrc
(that is, ifsrc
anddest
are on different file systems), ignores raised exceptions ofStandardError
and its descendants. -
noop: true
- does not move files. -
secure: true
- removessrc
securely; see details atFileUtils.remove_entry_secure
. -
verbose: true
- prints an equivalent command:FileUtils.mv('src0', 'dest0', noop: true, verbose: true) FileUtils.mv(['src1.txt', 'src1'], 'dest1', noop: true, verbose: true)
Output:
mv src0 dest0 mv src1.txt src1 dest1
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 2578
def self.options
OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
end
Returns an array of the string keyword names:
FileUtils.options.take(3) # => ["noop", "verbose", "force"]
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 2599
def self.options_of(mid)
OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
end
Returns an array of the string keyword name for method mid
; the argument may be a string or a symbol:
FileUtils.options_of(:rm) # => ["force", "noop", "verbose"] FileUtils.options_of('mv') # => ["force", "noop", "verbose", "secure"]
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 197
def pwd
Dir.pwd
end
Returns a string containing the path to the current directory:
FileUtils.pwd # => "/rdoc/fileutils"
Related: FileUtils.cd
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1492
def remove_dir(path, force = false)
remove_entry path, force # FIXME?? check if it is a directory
end
Recursively removes the directory entry given by path
, which should be the entry for a regular file, a symbolic link, or a directory.
Argument path
should be interpretable as a path.
Optional argument force
specifies whether to ignore raised exceptions of StandardError
and its descendants.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1449
def remove_entry(path, force = false)
Entry_.new(path).postorder_traverse do |ent|
begin
ent.remove
rescue
raise unless force
end
end
rescue
raise unless force
end
Removes the entry given by path
, which should be the entry for a regular file, a symbolic link, or a directory.
Argument path
should be interpretable as a path.
Optional argument force
specifies whether to ignore raised exceptions of StandardError
and its descendants.
Related: FileUtils.remove_entry_secure
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1351
def remove_entry_secure(path, force = false)
unless fu_have_symlink?
remove_entry path, force
return
end
fullpath = File.expand_path(path)
st = File.lstat(fullpath)
unless st.directory?
File.unlink fullpath
return
end
# is a directory.
parent_st = File.stat(File.dirname(fullpath))
unless parent_st.world_writable?
remove_entry path, force
return
end
unless parent_st.sticky?
raise ArgumentError, "parent directory is world writable, FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
end
# freeze tree root
euid = Process.euid
dot_file = fullpath + "/."
begin
File.open(dot_file) {|f|
unless fu_stat_identical_entry?(st, f.stat)
# symlink (TOC-to-TOU attack?)
File.unlink fullpath
return
end
f.chown euid, -1
f.chmod 0700
}
rescue Errno::EISDIR # JRuby in non-native mode can't open files as dirs
File.lstat(dot_file).tap {|fstat|
unless fu_stat_identical_entry?(st, fstat)
# symlink (TOC-to-TOU attack?)
File.unlink fullpath
return
end
File.chown euid, -1, dot_file
File.chmod 0700, dot_file
}
end
unless fu_stat_identical_entry?(st, File.lstat(fullpath))
# TOC-to-TOU attack?
File.unlink fullpath
return
end
# ---- tree root is frozen ----
root = Entry_.new(path)
root.preorder_traverse do |ent|
if ent.directory?
ent.chown euid, -1
ent.chmod 0700
end
end
root.postorder_traverse do |ent|
begin
ent.remove
rescue
raise unless force
end
end
rescue
raise unless force
end
Securely removes the entry given by path
, which should be the entry for a regular file, a symbolic link, or a directory.
Argument path
should be interpretable as a path.
Avoids a local vulnerability that can exist in certain circumstances; see Avoiding the TOCTTOU Vulnerability.
Optional argument force
specifies whether to ignore raised exceptions of StandardError
and its descendants.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1473
def remove_file(path, force = false)
Entry_.new(path).remove_file
rescue
raise unless force
end
Removes the file entry given by path
, which should be the entry for a regular file or a symbolic link.
Argument path
should be interpretable as a path.
Optional argument force
specifies whether to ignore raised exceptions of StandardError
and its descendants.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1216
def rm(list, force: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "rm#{force ? ' -f' : ''} #{list.join ' '}" if verbose
return if noop
list.each do |path|
remove_file path, force
end
end
Removes entries at the paths in the given list
(a single path or an array of paths) returns list
, if it is an array, [list]
otherwise.
Argument list
or its elements should be interpretable as paths.
With no keyword arguments, removes files at the paths given in list
:
FileUtils.touch(['src0.txt', 'src0.dat']) FileUtils.rm(['src0.dat', 'src0.txt']) # => ["src0.dat", "src0.txt"]
Keyword arguments:
-
force: true
- ignores raised exceptions ofStandardError
and its descendants. -
noop: true
- does not remove files; returnsnil
. -
verbose: true
- prints an equivalent command:FileUtils.rm(['src0.dat', 'src0.txt'], noop: true, verbose: true)
Output:
rm src0.dat src0.txt
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1241
def rm_f(list, noop: nil, verbose: nil)
rm list, force: true, noop: noop, verbose: verbose
end
Equivalent to:
FileUtils.rm(list, force: true, **kwargs)
Argument list
(a single path or an array of paths) should be interpretable as paths.
See FileUtils.rm
for keyword arguments.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1299
def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
list = fu_list(list)
fu_output_message "rm -r#{force ? 'f' : ''} #{list.join ' '}" if verbose
return if noop
list.each do |path|
if secure
remove_entry_secure path, force
else
remove_entry path, force
end
end
end
Removes entries at the paths in the given list
(a single path or an array of paths); returns list
, if it is an array, [list]
otherwise.
Argument list
or its elements should be interpretable as paths.
May cause a local vulnerability if not called with keyword argument secure: true
; see Avoiding the TOCTTOU Vulnerability.
For each file path, removes the file at that path:
FileUtils.touch(['src0.txt', 'src0.dat']) FileUtils.rm_r(['src0.dat', 'src0.txt']) File.exist?('src0.txt') # => false File.exist?('src0.dat') # => false
For each directory path, recursively removes files and directories:
tree('src1') # => src1 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt FileUtils.rm_r('src1') File.exist?('src1') # => false
Keyword arguments:
-
force: true
- ignores raised exceptions ofStandardError
and its descendants. -
noop: true
- does not remove entries; returnsnil
. -
secure: true
- removessrc
securely; see details atFileUtils.remove_entry_secure
. -
verbose: true
- prints an equivalent command:FileUtils.rm_r(['src0.dat', 'src0.txt'], noop: true, verbose: true) FileUtils.rm_r('src1', noop: true, verbose: true)
Output:
rm -r src0.dat src0.txt rm -r src1
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1328
def rm_rf(list, noop: nil, verbose: nil, secure: nil)
rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
end
Equivalent to:
FileUtils.rm_r(list, force: true, **kwargs)
Argument list
or its elements should be interpretable as paths.
May cause a local vulnerability if not called with keyword argument secure: true
; see Avoiding the TOCTTOU Vulnerability.
See FileUtils.rm_r
for keyword arguments.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 442
def rmdir(list, parents: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
return if noop
list.each do |dir|
Dir.rmdir(dir = remove_trailing_slash(dir))
if parents
begin
until (parent = File.dirname(dir)) == '.' or parent == dir
dir = parent
Dir.rmdir(dir)
end
rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
end
end
end
end
Removes directories at the paths in the given list
(a single path or an array of paths); returns list
, if it is an array, [list]
otherwise.
Argument list
or its elements should be interpretable as paths.
With no keyword arguments, removes the directory at each path
in list
, by calling: Dir.rmdir(path)
; see Dir.rmdir
:
FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"] FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"]
Keyword arguments:
-
parents: true
- removes successive ancestor directories if empty. -
noop: true
- does not remove directories. -
verbose: true
- prints an equivalent command:FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3], parents: true, verbose: true) FileUtils.rmdir('tmp4/tmp5', parents: true, verbose: true)
Output:
rmdir -p tmp0/tmp1 tmp2/tmp3 rmdir -p tmp4/tmp5
Raises an exception if a directory does not exist or if for any reason a directory cannot be removed.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 2006
def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
list = fu_list(list)
t = mtime
if verbose
fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}"
end
return if noop
list.each do |path|
created = nocreate
begin
File.utime(t, t, path)
rescue Errno::ENOENT
raise if created
File.open(path, 'a') {
;
}
created = true
retry if t
end
end
end
Updates modification times (mtime) and access times (atime) of the entries given by the paths in list
(a single path or an array of paths); returns list
if it is an array, [list]
otherwise.
By default, creates an empty file for any path to a non-existent entry; use keyword argument nocreate
to raise an exception instead.
Argument list
or its elements should be interpretable as paths.
Examples:
# Single path. f = File.new('src0.txt') # Existing file. f.atime # => 2022-06-10 11:11:21.200277 -0700 f.mtime # => 2022-06-10 11:11:21.200277 -0700 FileUtils.touch('src0.txt') f = File.new('src0.txt') f.atime # => 2022-06-11 08:28:09.8185343 -0700 f.mtime # => 2022-06-11 08:28:09.8185343 -0700 # Array of paths. FileUtils.touch(['src0.txt', 'src0.dat'])
Keyword arguments:
-
mtime: time
- sets the entry’s mtime to the given time, instead of the current time. -
nocreate: true
- raises an exception if the entry does not exist. -
noop: true
- does not touch entries; returnsnil
. -
verbose: true
- prints an equivalent command:FileUtils.touch('src0.txt', noop: true, verbose: true) FileUtils.touch(['src0.txt', 'src0.dat'], noop: true, verbose: true) FileUtils.touch(path, noop: true, verbose: true)
Output:
touch src0.txt touch src0.txt src0.dat touch src0.txt
Related: FileUtils.uptodate?
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 264
def uptodate?(new, old_list)
return false unless File.exist?(new)
new_time = File.mtime(new)
old_list.each do |old|
if File.exist?(old)
return false unless new_time > File.mtime(old)
end
end
true
end
Returns true
if the file at path new
is newer than all the files at paths in array old_list
; false
otherwise.
Argument new
and the elements of old_list
should be interpretable as paths:
FileUtils.uptodate?('Rakefile', ['Gemfile', 'README.md']) # => true FileUtils.uptodate?('Gemfile', ['Rakefile', 'README.md']) # => false
A non-existent file is considered to be infinitely old.
Related: FileUtils.touch
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 238
def cd(dir, verbose: nil, &block) # :yield: dir
fu_output_message "cd #{dir}" if verbose
result = Dir.chdir(dir, &block)
fu_output_message 'cd -' if verbose and block
result
end
Changes the working directory to the given dir
, which should be interpretable as a path:
With no block given, changes the current directory to the directory at dir
; returns zero:
FileUtils.pwd # => "/rdoc/fileutils" FileUtils.cd('..') FileUtils.pwd # => "/rdoc" FileUtils.cd('fileutils')
With a block given, changes the current directory to the directory at dir
, calls the block with argument dir
, and restores the original current directory; returns the block’s value:
FileUtils.pwd # => "/rdoc/fileutils" FileUtils.cd('..') { |arg| [arg, FileUtils.pwd] } # => ["..", "/rdoc"] FileUtils.pwd # => "/rdoc/fileutils"
Keyword arguments:
-
verbose: true
- prints an equivalent command:FileUtils.cd('..') FileUtils.cd('fileutils')
Output:
cd .. cd fileutils
Related: FileUtils.pwd
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1803
def chmod(mode, list, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
return if noop
list.each do |path|
Entry_.new(path).chmod(fu_mode(mode, path))
end
end
Changes permissions on the entries at the paths given in list
(a single path or an array of paths) to the permissions given by mode
; returns list
if it is an array, [list]
otherwise:
-
Modifies each entry that is a regular file using
File.chmod
. -
Modifies each entry that is a symbolic link using
File.lchmod
.
Argument list
or its elements should be interpretable as paths.
Argument mode
may be either an integer or a string:
-
Integer
mode
: represents the permission bits to be set:FileUtils.chmod(0755, 'src0.txt') FileUtils.chmod(0644, ['src0.txt', 'src0.dat'])
-
String
mode
: represents the permissions to be set:The string is of the form
[targets][[operator][perms[,perms]]
, where:-
targets
may be any combination of these letters:-
'u'
: permissions apply to the file’s owner. -
'g'
: permissions apply to users in the file’s group. -
'o'
: permissions apply to other users not in the file’s group. -
'a'
(the default): permissions apply to all users.
-
-
operator
may be one of these letters:-
'+'
: adds permissions. -
'-'
: removes permissions. -
'='
: sets (replaces) permissions.
-
-
perms
(may be repeated, with separating commas) may be any combination of these letters:-
'r'
: Read. -
'w'
: Write. -
'x'
: Execute (search, for a directory). -
'X'
: Search (for a directories only; must be used with'+'
) -
's'
: Uid or gid. -
't'
: Sticky bit.
-
Examples:
FileUtils.chmod('u=wrx,go=rx', 'src1.txt') FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby')
-
Keyword arguments:
-
noop: true
- does not change permissions; returnsnil
. -
verbose: true
- prints an equivalent command:FileUtils.chmod(0755, 'src0.txt', noop: true, verbose: true) FileUtils.chmod(0644, ['src0.txt', 'src0.dat'], noop: true, verbose: true) FileUtils.chmod('u=wrx,go=rx', 'src1.txt', noop: true, verbose: true) FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby', noop: true, verbose: true)
Output:
chmod 755 src0.txt chmod 644 src0.txt src0.dat chmod u=wrx,go=rx src1.txt chmod u=wrx,go=rx /usr/bin/ruby
Related: FileUtils.chmod_R
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1815
def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
fu_output_message sprintf('chmod -R%s %s %s',
(force ? 'f' : ''),
mode_to_s(mode), list.join(' ')) if verbose
return if noop
list.each do |root|
Entry_.new(root).traverse do |ent|
begin
ent.chmod(fu_mode(mode, ent.path))
rescue
raise unless force
end
end
end
end
Like FileUtils.chmod
, but changes permissions recursively.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1896
def chown(user, group, list, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message sprintf('chown %s %s',
(group ? "#{user}:#{group}" : user || ':'),
list.join(' ')) if verbose
return if noop
uid = fu_get_uid(user)
gid = fu_get_gid(group)
list.each do |path|
Entry_.new(path).chown uid, gid
end
end
Changes the owner and group on the entries at the paths given in list
(a single path or an array of paths) to the given user
and group
; returns list
if it is an array, [list]
otherwise:
-
Modifies each entry that is a regular file using
File.chown
. -
Modifies each entry that is a symbolic link using
File.lchown
.
Argument list
or its elements should be interpretable as paths.
User and group:
-
Argument
user
may be a user name or a user id; ifnil
or-1
, the user is not changed. -
Argument
group
may be a group name or a group id; ifnil
or-1
, the group is not changed. -
The user must be a member of the group.
Examples:
# One path. # User and group as string names. File.stat('src0.txt').uid # => 1004 File.stat('src0.txt').gid # => 1004 FileUtils.chown('user2', 'group1', 'src0.txt') File.stat('src0.txt').uid # => 1006 File.stat('src0.txt').gid # => 1005 # User and group as uid and gid. FileUtils.chown(1004, 1004, 'src0.txt') File.stat('src0.txt').uid # => 1004 File.stat('src0.txt').gid # => 1004 # Array of paths. FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat']) # Directory (not recursive). FileUtils.chown('user2', 'group1', '.')
Keyword arguments:
-
noop: true
- does not change permissions; returnsnil
. -
verbose: true
- prints an equivalent command:FileUtils.chown('user2', 'group1', 'src0.txt', noop: true, verbose: true) FileUtils.chown(1004, 1004, 'src0.txt', noop: true, verbose: true) FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'], noop: true, verbose: true) FileUtils.chown('user2', 'group1', path, noop: true, verbose: true) FileUtils.chown('user2', 'group1', '.', noop: true, verbose: true)
Output:
chown user2:group1 src0.txt chown 1004:1004 src0.txt chown 1006:1005 src0.txt src0.dat chown user2:group1 src0.txt chown user2:group1 .
Related: FileUtils.chown_R
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1912
def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
fu_output_message sprintf('chown -R%s %s %s',
(force ? 'f' : ''),
(group ? "#{user}:#{group}" : user || ':'),
list.join(' ')) if verbose
return if noop
uid = fu_get_uid(user)
gid = fu_get_gid(group)
list.each do |root|
Entry_.new(root).traverse do |ent|
begin
ent.chown uid, gid
rescue
raise unless force
end
end
end
end
Like FileUtils.chown
, but changes owner and group recursively.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1507
def compare_file(a, b)
return false unless File.size(a) == File.size(b)
File.open(a, 'rb') {|fa|
File.open(b, 'rb') {|fb|
return compare_stream(fa, fb)
}
}
end
Returns true
if the contents of files a
and b
are identical, false
otherwise.
Arguments a
and b
should be interpretable as a path.
FileUtils.identical?
and FileUtils.cmp
are aliases for FileUtils.compare_file
.
Related: FileUtils.compare_stream
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1530
def compare_stream(a, b)
bsize = fu_stream_blksize(a, b)
sa = String.new(capacity: bsize)
sb = String.new(capacity: bsize)
begin
a.read(bsize, sa)
b.read(bsize, sb)
return true if sa.empty? && sb.empty?
end while sa == sb
false
end
Returns true
if the contents of streams a
and b
are identical, false
otherwise.
Arguments a
and b
should be interpretable as a path.
Related: FileUtils.compare_file
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1040
def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
if dereference_root
src = File.realpath(src)
end
Entry_.new(src, nil, false).wrap_traverse(proc do |ent|
destent = Entry_.new(dest, ent.rel, false)
File.unlink destent.path if remove_destination && (File.file?(destent.path) || File.symlink?(destent.path))
ent.copy destent.path
end, proc do |ent|
destent = Entry_.new(dest, ent.rel, false)
ent.copy_metadata destent.path if preserve
end)
end
Recursively copies files from src
to dest
.
Arguments src
and dest
should be interpretable as paths.
If src
is the path to a file, copies src
to dest
:
FileUtils.touch('src0.txt') File.exist?('dest0.txt') # => false FileUtils.copy_entry('src0.txt', 'dest0.txt') File.file?('dest0.txt') # => true
If src
is a directory, recursively copies src
to dest
:
tree('src1') # => src1 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt FileUtils.copy_entry('src1', 'dest1') tree('dest1') # => dest1 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt
The recursive copying preserves file types for regular files, directories, and symbolic links; other file types (FIFO streams, device files, etc.) are not supported.
Keyword arguments:
-
dereference_root: true
- ifsrc
is a symbolic link, follows the link. -
preserve: true
- preserves file times. -
remove_destination: true
- removesdest
before copying files.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1076
def copy_file(src, dest, preserve = false, dereference = true)
ent = Entry_.new(src, nil, dereference)
ent.copy_file dest
ent.copy_metadata dest if preserve
end
Copies file from src
to dest
, which should not be directories.
Arguments src
and dest
should be interpretable as paths.
Examples:
FileUtils.touch('src0.txt') FileUtils.copy_file('src0.txt', 'dest0.txt') File.file?('dest0.txt') # => true
Keyword arguments:
-
dereference: false
- ifsrc
is a symbolic link, does not follow the link. -
preserve: true
- preserves file times. -
remove_destination: true
- removesdest
before copying files.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1088
def copy_stream(src, dest)
IO.copy_stream(src, dest)
end
Copies IO stream src
to IO stream dest
via IO.copy_stream
.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 873
def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
copy_file s, d, preserve
end
end
Copies files.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
If src
is the path to a file and dest
is not the path to a directory, copies src
to dest
:
FileUtils.touch('src0.txt') File.exist?('dest0.txt') # => false FileUtils.cp('src0.txt', 'dest0.txt') File.file?('dest0.txt') # => true
If src
is the path to a file and dest
is the path to a directory, copies src
to dest/src
:
FileUtils.touch('src1.txt') FileUtils.mkdir('dest1') FileUtils.cp('src1.txt', 'dest1') File.file?('dest1/src1.txt') # => true
If src
is an array of paths to files and dest
is the path to a directory, copies from each src
to dest
:
src_file_paths = ['src2.txt', 'src2.dat'] FileUtils.touch(src_file_paths) FileUtils.mkdir('dest2') FileUtils.cp(src_file_paths, 'dest2') File.file?('dest2/src2.txt') # => true File.file?('dest2/src2.dat') # => true
Keyword arguments:
-
preserve: true
- preserves file times. -
noop: true
- does not copy files. -
verbose: true
- prints an equivalent command:FileUtils.cp('src0.txt', 'dest0.txt', noop: true, verbose: true) FileUtils.cp('src1.txt', 'dest1', noop: true, verbose: true) FileUtils.cp(src_file_paths, 'dest2', noop: true, verbose: true)
Output:
cp src0.txt dest0.txt cp src1.txt dest1 cp src2.txt src2.dat dest2
Raises an exception if src
is a directory.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 627
def cp_lr(src, dest, noop: nil, verbose: nil,
dereference_root: true, remove_destination: false)
fu_output_message "cp -lr#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
link_entry s, d, dereference_root, remove_destination
end
end
Creates hard links.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
If src
is the path to a directory and dest
does not exist, creates links dest
and descendents pointing to src
and its descendents:
tree('src0') # => src0 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt File.exist?('dest0') # => false FileUtils.cp_lr('src0', 'dest0') tree('dest0') # => dest0 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt
If src
and dest
are both paths to directories, creates links dest/src
and descendents pointing to src
and its descendents:
tree('src1') # => src1 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt FileUtils.mkdir('dest1') FileUtils.cp_lr('src1', 'dest1') tree('dest1') # => dest1 # `-- src1 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt
If src
is an array of paths to entries and dest
is the path to a directory, for each path filepath
in src
, creates a link at dest/filepath
pointing to that path:
tree('src2') # => src2 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt FileUtils.mkdir('dest2') FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2') tree('dest2') # => dest2 # |-- sub0 # | |-- src0.txt # | `-- src1.txt # `-- sub1 # |-- src2.txt # `-- src3.txt
Keyword arguments:
-
dereference_root: false
- ifsrc
is a symbolic link, does not dereference it. -
noop: true
- does not create links. -
remove_destination: true
- removesdest
before creating links. -
verbose: true
- prints an equivalent command:FileUtils.cp_lr('src0', 'dest0', noop: true, verbose: true) FileUtils.cp_lr('src1', 'dest1', noop: true, verbose: true) FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2', noop: true, verbose: true)
Output:
cp -lr src0 dest0 cp -lr src1 dest1 cp -lr src2/sub0 src2/sub1 dest2
Raises an exception if dest
is the path to an existing file or directory and keyword argument remove_destination: true
is not given.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 985
def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
dereference_root: true, remove_destination: nil)
fu_output_message "cp -r#{preserve ? 'p' : ''}#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
copy_entry s, d, preserve, dereference_root, remove_destination
end
end
Recursively copies files.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
The mode, owner, and group are retained in the copy; to change those, use FileUtils.install
instead.
If src
is the path to a file and dest
is not the path to a directory, copies src
to dest
:
FileUtils.touch('src0.txt') File.exist?('dest0.txt') # => false FileUtils.cp_r('src0.txt', 'dest0.txt') File.file?('dest0.txt') # => true
If src
is the path to a file and dest
is the path to a directory, copies src
to dest/src
:
FileUtils.touch('src1.txt') FileUtils.mkdir('dest1') FileUtils.cp_r('src1.txt', 'dest1') File.file?('dest1/src1.txt') # => true
If src
is the path to a directory and dest
does not exist, recursively copies src
to dest
:
tree('src2') # => src2 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt FileUtils.exist?('dest2') # => false FileUtils.cp_r('src2', 'dest2') tree('dest2') # => dest2 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt
If src
and dest
are paths to directories, recursively copies src
to dest/src
:
tree('src3') # => src3 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt FileUtils.mkdir('dest3') FileUtils.cp_r('src3', 'dest3') tree('dest3') # => dest3 # `-- src3 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt
If src
is an array of paths and dest
is a directory, recursively copies from each path in src
to dest
; the paths in src
may point to files and/or directories.
Keyword arguments:
-
dereference_root: false
- ifsrc
is a symbolic link, does not dereference it. -
noop: true
- does not copy files. -
preserve: true
- preserves file times. -
remove_destination: true
- removesdest
before copying files. -
verbose: true
- prints an equivalent command:FileUtils.cp_r('src0.txt', 'dest0.txt', noop: true, verbose: true) FileUtils.cp_r('src1.txt', 'dest1', noop: true, verbose: true) FileUtils.cp_r('src2', 'dest2', noop: true, verbose: true) FileUtils.cp_r('src3', 'dest3', noop: true, verbose: true)
Output:
cp -r src0.txt dest0.txt cp -r src1.txt dest1 cp -r src2 dest2 cp -r src3 dest3
Raises an exception of src
is the path to a directory and dest
is the path to a file.
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 2527
def fu_clean_components(*comp)
comp.shift while comp.first == "."
return comp if comp.empty?
clean = [comp.shift]
path = File.join(*clean, "") # ending with File::SEPARATOR
while c = comp.shift
if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
clean.pop
path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
else
clean << c
path << c << "/"
end
end
clean
end
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 2506
def fu_split_path(path)
path = File.path(path)
list = []
until (parent, base = File.split(path); parent == path or parent == ".")
list << base
path = parent
end
list << path
list.reverse!
end
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 2546
def fu_starting_path?(path)
path&.start_with?(%r(\w:|/))
end
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1609
def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
noop: nil, verbose: nil)
if verbose
msg = +"install -c"
msg << ' -p' if preserve
msg << ' -m ' << mode_to_s(mode) if mode
msg << " -o #{owner}" if owner
msg << " -g #{group}" if group
msg << ' ' << [src,dest].flatten.join(' ')
fu_output_message msg
end
return if noop
uid = fu_get_uid(owner)
gid = fu_get_gid(group)
fu_each_src_dest(src, dest) do |s, d|
st = File.stat(s)
unless File.exist?(d) and compare_file(s, d)
remove_file d, true
if d.end_with?('/')
mkdir_p d
copy_file s, d + File.basename(s)
else
mkdir_p File.expand_path('..', d)
copy_file s, d
end
File.utime st.atime, st.mtime, d if preserve
File.chmod fu_mode(mode, st), d if mode
File.chown uid, gid, d if uid or gid
end
end
end
Copies a file entry. See install(1).
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths;
If the entry at dest
does not exist, copies from src
to dest
:
File.read('src0.txt') # => "aaa\n" File.exist?('dest0.txt') # => false FileUtils.install('src0.txt', 'dest0.txt') File.read('dest0.txt') # => "aaa\n"
If dest
is a file entry, copies from src
to dest
, overwriting:
File.read('src1.txt') # => "aaa\n" File.read('dest1.txt') # => "bbb\n" FileUtils.install('src1.txt', 'dest1.txt') File.read('dest1.txt') # => "aaa\n"
If dest
is a directory entry, copies from src
to dest/src
, overwriting if necessary:
File.read('src2.txt') # => "aaa\n" File.read('dest2/src2.txt') # => "bbb\n" FileUtils.install('src2.txt', 'dest2') File.read('dest2/src2.txt') # => "aaa\n"
If src
is an array of paths and dest
points to a directory, copies each path path
in src
to dest/path
:
File.file?('src3.txt') # => true File.file?('src3.dat') # => true FileUtils.mkdir('dest3') FileUtils.install(['src3.txt', 'src3.dat'], 'dest3') File.file?('dest3/src3.txt') # => true File.file?('dest3/src3.dat') # => true
Keyword arguments:
-
group: group
- changes the group if notnil
, usingFile.chown
. -
mode: permissions
- changes the permissions. usingFile.chmod
. -
noop: true
- does not copy entries; returnsnil
. -
owner: owner
- changes the owner if notnil
, usingFile.chown
. -
preserve: true
- preserve timestamps usingFile.utime
. -
verbose: true
- prints an equivalent command:FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true) FileUtils.install('src1.txt', 'dest1.txt', noop: true, verbose: true) FileUtils.install('src2.txt', 'dest2', noop: true, verbose: true)
Output:
install -c src0.txt dest0.txt install -c src1.txt dest1.txt install -c src2.txt dest2
Related: methods for copying.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 812
def link_entry(src, dest, dereference_root = false, remove_destination = false)
Entry_.new(src, nil, dereference_root).traverse do |ent|
destent = Entry_.new(dest, ent.rel, false)
File.unlink destent.path if remove_destination && File.file?(destent.path)
ent.link destent.path
end
end
Creates hard links; returns nil
.
Arguments src
and dest
should be interpretable as paths.
If src
is the path to a file and dest
does not exist, creates a hard link at dest
pointing to src
:
FileUtils.touch('src0.txt') File.exist?('dest0.txt') # => false FileUtils.link_entry('src0.txt', 'dest0.txt') File.file?('dest0.txt') # => true
If src
is the path to a directory and dest
does not exist, recursively creates hard links at dest
pointing to paths in src
:
FileUtils.mkdir_p(['src1/dir0', 'src1/dir1']) src_file_paths = [ 'src1/dir0/t0.txt', 'src1/dir0/t1.txt', 'src1/dir1/t2.txt', 'src1/dir1/t3.txt', ] FileUtils.touch(src_file_paths) File.directory?('dest1') # => true FileUtils.link_entry('src1', 'dest1') File.file?('dest1/dir0/t0.txt') # => true File.file?('dest1/dir0/t1.txt') # => true File.file?('dest1/dir1/t2.txt') # => true File.file?('dest1/dir1/t3.txt') # => true
Keyword arguments:
-
dereference_root: true
- dereferencessrc
if it is a symbolic link. -
remove_destination: true
- removesdest
before creating links.
Raises an exception if dest
is the path to an existing file or directory and keyword argument remove_destination: true
is not given.
Related: FileUtils.ln
(has different options).
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 516
def ln(src, dest, force: nil, noop: nil, verbose: nil)
fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
remove_file d, true if force
File.link s, d
end
end
Creates hard links.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
When src
is the path to an existing file and dest
is the path to a non-existent file, creates a hard link at dest
pointing to src
; returns zero:
Dir.children('tmp0/') # => ["t.txt"] Dir.children('tmp1/') # => [] FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk') # => 0 Dir.children('tmp1/') # => ["t.lnk"]
When src
is the path to an existing file and dest
is the path to an existing directory, creates a hard link at dest/src
pointing to src
; returns zero:
Dir.children('tmp2') # => ["t.dat"] Dir.children('tmp3') # => [] FileUtils.ln('tmp2/t.dat', 'tmp3') # => 0 Dir.children('tmp3') # => ["t.dat"]
When src
is an array of paths to existing files and dest
is the path to an existing directory, then for each path target
in src
, creates a hard link at dest/target
pointing to target
; returns src
:
Dir.children('tmp4/') # => [] FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/') # => ["tmp0/t.txt", "tmp2/t.dat"] Dir.children('tmp4/') # => ["t.dat", "t.txt"]
Keyword arguments:
-
force: true
- overwritesdest
if it exists. -
noop: true
- does not create links. -
verbose: true
- prints an equivalent command:FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk', verbose: true) FileUtils.ln('tmp2/t.dat', 'tmp3', verbose: true) FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/', verbose: true)
Output:
ln tmp0/t.txt tmp1/t.lnk ln tmp2/t.dat tmp3 ln tmp0/t.txt tmp2/t.dat tmp4/
Raises an exception if dest
is the path to an existing file and keyword argument force
is not true
.
Related: FileUtils.link_entry
(has different options).
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 706
def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
if relative
return ln_sr(src, dest, force: force, noop: noop, verbose: verbose)
end
fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest0(src, dest) do |s,d|
remove_file d, true if force
File.symlink s, d
end
end
Creates symbolic links.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
If src
is the path to an existing file:
-
When
dest
is the path to a non-existent file, creates a symbolic link atdest
pointing tosrc
:FileUtils.touch('src0.txt') File.exist?('dest0.txt') # => false FileUtils.ln_s('src0.txt', 'dest0.txt') File.symlink?('dest0.txt') # => true
-
When
dest
is the path to an existing file, creates a symbolic link atdest
pointing tosrc
if and only if keyword argumentforce: true
is given (raises an exception otherwise):FileUtils.touch('src1.txt') FileUtils.touch('dest1.txt') FileUtils.ln_s('src1.txt', 'dest1.txt', force: true) FileTest.symlink?('dest1.txt') # => true FileUtils.ln_s('src1.txt', 'dest1.txt') # Raises Errno::EEXIST.
If dest
is the path to a directory, creates a symbolic link at dest/src
pointing to src
:
FileUtils.touch('src2.txt') FileUtils.mkdir('destdir2') FileUtils.ln_s('src2.txt', 'destdir2') File.symlink?('destdir2/src2.txt') # => true
If src
is an array of paths to existing files and dest
is a directory, for each child child
in src
creates a symbolic link dest/child
pointing to child
:
FileUtils.mkdir('srcdir3') FileUtils.touch('srcdir3/src0.txt') FileUtils.touch('srcdir3/src1.txt') FileUtils.mkdir('destdir3') FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3') File.symlink?('destdir3/src0.txt') # => true File.symlink?('destdir3/src1.txt') # => true
Keyword arguments:
-
force: true
- overwritesdest
if it exists. -
relative: false
- create links relative todest
. -
noop: true
- does not create links. -
verbose: true
- prints an equivalent command:FileUtils.ln_s('src0.txt', 'dest0.txt', noop: true, verbose: true) FileUtils.ln_s('src1.txt', 'destdir1', noop: true, verbose: true) FileUtils.ln_s('src2.txt', 'dest2.txt', force: true, noop: true, verbose: true) FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3', noop: true, verbose: true)
Output:
ln -s src0.txt dest0.txt ln -s src1.txt destdir1 ln -sf src2.txt dest2.txt ln -s srcdir3/src0.txt srcdir3/src1.txt destdir3
Related: FileUtils.ln_sf
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 724
def ln_sf(src, dest, noop: nil, verbose: nil)
ln_s src, dest, force: true, noop: noop, verbose: verbose
end
Like FileUtils.ln_s
, but always with keyword argument force: true
given.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 731
def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
options = "#{force ? 'f' : ''}#{target_directory ? '' : 'T'}"
dest = File.path(dest)
srcs = Array(src)
link = proc do |s, target_dir_p = true|
s = File.path(s)
if target_dir_p
d = File.join(destdirs = dest, File.basename(s))
else
destdirs = File.dirname(d = dest)
end
destdirs = fu_split_path(File.realpath(destdirs))
if fu_starting_path?(s)
srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
base = fu_relative_components_from(srcdirs, destdirs)
s = File.join(*base)
else
srcdirs = fu_clean_components(*fu_split_path(s))
base = fu_relative_components_from(fu_split_path(Dir.pwd), destdirs)
while srcdirs.first&. == ".." and base.last&.!=("..") and !fu_starting_path?(base.last)
srcdirs.shift
base.pop
end
s = File.join(*base, *srcdirs)
end
fu_output_message "ln -s#{options} #{s} #{d}" if verbose
next if noop
remove_file d, true if force
File.symlink s, d
end
case srcs.size
when 0
when 1
link[srcs[0], target_directory && File.directory?(dest)]
else
srcs.each(&link)
end
end
Like FileUtils.ln_s
, but create links relative to dest
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 316
def mkdir(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "mkdir #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
return if noop
list.each do |dir|
fu_mkdir dir, mode
end
end
Creates directories at the paths in the given list
(a single path or an array of paths); returns list
if it is an array, [list]
otherwise.
Argument list
or its elements should be interpretable as paths.
With no keyword arguments, creates a directory at each path
in list
by calling: Dir.mkdir(path, mode)
; see Dir.mkdir
:
FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"] FileUtils.mkdir('tmp4') # => ["tmp4"]
Keyword arguments:
-
mode: mode
- also callsFile.chmod(mode, path)
; seeFile.chmod
. -
noop: true
- does not create directories. -
verbose: true
- prints an equivalent command:FileUtils.mkdir(%w[tmp0 tmp1], verbose: true) FileUtils.mkdir(%w[tmp2 tmp3], mode: 0700, verbose: true)
Output:
mkdir tmp0 tmp1 mkdir -m 700 tmp2 tmp3
Raises an exception if any path points to an existing file or directory, or if for any reason a directory cannot be created.
Related: FileUtils.mkdir_p
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 365
def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose
return *list if noop
list.each do |item|
path = remove_trailing_slash(item)
stack = []
until File.directory?(path) || File.dirname(path) == path
stack.push path
path = File.dirname(path)
end
stack.reverse_each do |dir|
begin
fu_mkdir dir, mode
rescue SystemCallError
raise unless File.directory?(dir)
end
end
end
return *list
end
Creates directories at the paths in the given list
(a single path or an array of paths), also creating ancestor directories as needed; returns list
if it is an array, [list]
otherwise.
Argument list
or its elements should be interpretable as paths.
With no keyword arguments, creates a directory at each path
in list
, along with any needed ancestor directories, by calling: Dir.mkdir(path, mode)
; see Dir.mkdir
:
FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"] FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"]
Keyword arguments:
-
mode: mode
- also callsFile.chmod(mode, path)
; seeFile.chmod
. -
noop: true
- does not create directories. -
verbose: true
- prints an equivalent command:FileUtils.mkdir_p(%w[tmp0 tmp1], verbose: true) FileUtils.mkdir_p(%w[tmp2 tmp3], mode: 0700, verbose: true)
Output:
mkdir -p tmp0 tmp1 mkdir -p -m 700 tmp2 tmp3
Raises an exception if for any reason a directory cannot be created.
FileUtils.mkpath
and FileUtils.makedirs
are aliases for FileUtils.mkdir_p
.
Related: FileUtils.mkdir
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1157
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
fu_each_src_dest(src, dest) do |s, d|
destent = Entry_.new(d, nil, true)
begin
if destent.exist?
if destent.directory?
raise Errno::EEXIST, d
end
end
begin
File.rename s, d
rescue Errno::EXDEV,
Errno::EPERM # move from unencrypted to encrypted dir (ext4)
copy_entry s, d, true
if secure
remove_entry_secure s, force
else
remove_entry s, force
end
end
rescue SystemCallError
raise unless force
end
end
end
Moves entries.
Arguments src
(a single path or an array of paths) and dest
(a single path) should be interpretable as paths.
If src
and dest
are on different file systems, first copies, then removes src
.
May cause a local vulnerability if not called with keyword argument secure: true
; see Avoiding the TOCTTOU Vulnerability.
If src
is the path to a single file or directory and dest
does not exist, moves src
to dest
:
tree('src0') # => src0 # |-- src0.txt # `-- src1.txt File.exist?('dest0') # => false FileUtils.mv('src0', 'dest0') File.exist?('src0') # => false tree('dest0') # => dest0 # |-- src0.txt # `-- src1.txt
If src
is an array of paths to files and directories and dest
is the path to a directory, copies from each path in the array to dest
:
File.file?('src1.txt') # => true tree('src1') # => src1 # |-- src.dat # `-- src.txt Dir.empty?('dest1') # => true FileUtils.mv(['src1.txt', 'src1'], 'dest1') tree('dest1') # => dest1 # |-- src1 # | |-- src.dat # | `-- src.txt # `-- src1.txt
Keyword arguments:
-
force: true
- if the move includes removingsrc
(that is, ifsrc
anddest
are on different file systems), ignores raised exceptions ofStandardError
and its descendants. -
noop: true
- does not move files. -
secure: true
- removessrc
securely; see details atFileUtils.remove_entry_secure
. -
verbose: true
- prints an equivalent command:FileUtils.mv('src0', 'dest0', noop: true, verbose: true) FileUtils.mv(['src1.txt', 'src1'], 'dest1', noop: true, verbose: true)
Output:
mv src0 dest0 mv src1.txt src1 dest1
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 197
def pwd
Dir.pwd
end
Returns a string containing the path to the current directory:
FileUtils.pwd # => "/rdoc/fileutils"
Related: FileUtils.cd
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1492
def remove_dir(path, force = false)
remove_entry path, force # FIXME?? check if it is a directory
end
Recursively removes the directory entry given by path
, which should be the entry for a regular file, a symbolic link, or a directory.
Argument path
should be interpretable as a path.
Optional argument force
specifies whether to ignore raised exceptions of StandardError
and its descendants.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1449
def remove_entry(path, force = false)
Entry_.new(path).postorder_traverse do |ent|
begin
ent.remove
rescue
raise unless force
end
end
rescue
raise unless force
end
Removes the entry given by path
, which should be the entry for a regular file, a symbolic link, or a directory.
Argument path
should be interpretable as a path.
Optional argument force
specifies whether to ignore raised exceptions of StandardError
and its descendants.
Related: FileUtils.remove_entry_secure
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1351
def remove_entry_secure(path, force = false)
unless fu_have_symlink?
remove_entry path, force
return
end
fullpath = File.expand_path(path)
st = File.lstat(fullpath)
unless st.directory?
File.unlink fullpath
return
end
# is a directory.
parent_st = File.stat(File.dirname(fullpath))
unless parent_st.world_writable?
remove_entry path, force
return
end
unless parent_st.sticky?
raise ArgumentError, "parent directory is world writable, FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
end
# freeze tree root
euid = Process.euid
dot_file = fullpath + "/."
begin
File.open(dot_file) {|f|
unless fu_stat_identical_entry?(st, f.stat)
# symlink (TOC-to-TOU attack?)
File.unlink fullpath
return
end
f.chown euid, -1
f.chmod 0700
}
rescue Errno::EISDIR # JRuby in non-native mode can't open files as dirs
File.lstat(dot_file).tap {|fstat|
unless fu_stat_identical_entry?(st, fstat)
# symlink (TOC-to-TOU attack?)
File.unlink fullpath
return
end
File.chown euid, -1, dot_file
File.chmod 0700, dot_file
}
end
unless fu_stat_identical_entry?(st, File.lstat(fullpath))
# TOC-to-TOU attack?
File.unlink fullpath
return
end
# ---- tree root is frozen ----
root = Entry_.new(path)
root.preorder_traverse do |ent|
if ent.directory?
ent.chown euid, -1
ent.chmod 0700
end
end
root.postorder_traverse do |ent|
begin
ent.remove
rescue
raise unless force
end
end
rescue
raise unless force
end
Securely removes the entry given by path
, which should be the entry for a regular file, a symbolic link, or a directory.
Argument path
should be interpretable as a path.
Avoids a local vulnerability that can exist in certain circumstances; see Avoiding the TOCTTOU Vulnerability.
Optional argument force
specifies whether to ignore raised exceptions of StandardError
and its descendants.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1473
def remove_file(path, force = false)
Entry_.new(path).remove_file
rescue
raise unless force
end
Removes the file entry given by path
, which should be the entry for a regular file or a symbolic link.
Argument path
should be interpretable as a path.
Optional argument force
specifies whether to ignore raised exceptions of StandardError
and its descendants.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1216
def rm(list, force: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "rm#{force ? ' -f' : ''} #{list.join ' '}" if verbose
return if noop
list.each do |path|
remove_file path, force
end
end
Removes entries at the paths in the given list
(a single path or an array of paths) returns list
, if it is an array, [list]
otherwise.
Argument list
or its elements should be interpretable as paths.
With no keyword arguments, removes files at the paths given in list
:
FileUtils.touch(['src0.txt', 'src0.dat']) FileUtils.rm(['src0.dat', 'src0.txt']) # => ["src0.dat", "src0.txt"]
Keyword arguments:
-
force: true
- ignores raised exceptions ofStandardError
and its descendants. -
noop: true
- does not remove files; returnsnil
. -
verbose: true
- prints an equivalent command:FileUtils.rm(['src0.dat', 'src0.txt'], noop: true, verbose: true)
Output:
rm src0.dat src0.txt
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1241
def rm_f(list, noop: nil, verbose: nil)
rm list, force: true, noop: noop, verbose: verbose
end
Equivalent to:
FileUtils.rm(list, force: true, **kwargs)
Argument list
(a single path or an array of paths) should be interpretable as paths.
See FileUtils.rm
for keyword arguments.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1299
def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
list = fu_list(list)
fu_output_message "rm -r#{force ? 'f' : ''} #{list.join ' '}" if verbose
return if noop
list.each do |path|
if secure
remove_entry_secure path, force
else
remove_entry path, force
end
end
end
Removes entries at the paths in the given list
(a single path or an array of paths); returns list
, if it is an array, [list]
otherwise.
Argument list
or its elements should be interpretable as paths.
May cause a local vulnerability if not called with keyword argument secure: true
; see Avoiding the TOCTTOU Vulnerability.
For each file path, removes the file at that path:
FileUtils.touch(['src0.txt', 'src0.dat']) FileUtils.rm_r(['src0.dat', 'src0.txt']) File.exist?('src0.txt') # => false File.exist?('src0.dat') # => false
For each directory path, recursively removes files and directories:
tree('src1') # => src1 # |-- dir0 # | |-- src0.txt # | `-- src1.txt # `-- dir1 # |-- src2.txt # `-- src3.txt FileUtils.rm_r('src1') File.exist?('src1') # => false
Keyword arguments:
-
force: true
- ignores raised exceptions ofStandardError
and its descendants. -
noop: true
- does not remove entries; returnsnil
. -
secure: true
- removessrc
securely; see details atFileUtils.remove_entry_secure
. -
verbose: true
- prints an equivalent command:FileUtils.rm_r(['src0.dat', 'src0.txt'], noop: true, verbose: true) FileUtils.rm_r('src1', noop: true, verbose: true)
Output:
rm -r src0.dat src0.txt rm -r src1
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 1328
def rm_rf(list, noop: nil, verbose: nil, secure: nil)
rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
end
Equivalent to:
FileUtils.rm_r(list, force: true, **kwargs)
Argument list
or its elements should be interpretable as paths.
May cause a local vulnerability if not called with keyword argument secure: true
; see Avoiding the TOCTTOU Vulnerability.
See FileUtils.rm_r
for keyword arguments.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 442
def rmdir(list, parents: nil, noop: nil, verbose: nil)
list = fu_list(list)
fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose
return if noop
list.each do |dir|
Dir.rmdir(dir = remove_trailing_slash(dir))
if parents
begin
until (parent = File.dirname(dir)) == '.' or parent == dir
dir = parent
Dir.rmdir(dir)
end
rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT
end
end
end
end
Removes directories at the paths in the given list
(a single path or an array of paths); returns list
, if it is an array, [list]
otherwise.
Argument list
or its elements should be interpretable as paths.
With no keyword arguments, removes the directory at each path
in list
, by calling: Dir.rmdir(path)
; see Dir.rmdir
:
FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"] FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"]
Keyword arguments:
-
parents: true
- removes successive ancestor directories if empty. -
noop: true
- does not remove directories. -
verbose: true
- prints an equivalent command:FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3], parents: true, verbose: true) FileUtils.rmdir('tmp4/tmp5', parents: true, verbose: true)
Output:
rmdir -p tmp0/tmp1 tmp2/tmp3 rmdir -p tmp4/tmp5
Raises an exception if a directory does not exist or if for any reason a directory cannot be removed.
Related: methods for deleting.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 2006
def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
list = fu_list(list)
t = mtime
if verbose
fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}"
end
return if noop
list.each do |path|
created = nocreate
begin
File.utime(t, t, path)
rescue Errno::ENOENT
raise if created
File.open(path, 'a') {
;
}
created = true
retry if t
end
end
end
Updates modification times (mtime) and access times (atime) of the entries given by the paths in list
(a single path or an array of paths); returns list
if it is an array, [list]
otherwise.
By default, creates an empty file for any path to a non-existent entry; use keyword argument nocreate
to raise an exception instead.
Argument list
or its elements should be interpretable as paths.
Examples:
# Single path. f = File.new('src0.txt') # Existing file. f.atime # => 2022-06-10 11:11:21.200277 -0700 f.mtime # => 2022-06-10 11:11:21.200277 -0700 FileUtils.touch('src0.txt') f = File.new('src0.txt') f.atime # => 2022-06-11 08:28:09.8185343 -0700 f.mtime # => 2022-06-11 08:28:09.8185343 -0700 # Array of paths. FileUtils.touch(['src0.txt', 'src0.dat'])
Keyword arguments:
-
mtime: time
- sets the entry’s mtime to the given time, instead of the current time. -
nocreate: true
- raises an exception if the entry does not exist. -
noop: true
- does not touch entries; returnsnil
. -
verbose: true
- prints an equivalent command:FileUtils.touch('src0.txt', noop: true, verbose: true) FileUtils.touch(['src0.txt', 'src0.dat'], noop: true, verbose: true) FileUtils.touch(path, noop: true, verbose: true)
Output:
touch src0.txt touch src0.txt src0.dat touch src0.txt
Related: FileUtils.uptodate?
.
# File tmp/rubies/ruby-3.3.0/lib/fileutils.rb, line 264
def uptodate?(new, old_list)
return false unless File.exist?(new)
new_time = File.mtime(new)
old_list.each do |old|
if File.exist?(old)
return false unless new_time > File.mtime(old)
end
end
true
end
Returns true
if the file at path new
is newer than all the files at paths in array old_list
; false
otherwise.
Argument new
and the elements of old_list
should be interpretable as paths:
FileUtils.uptodate?('Rakefile', ['Gemfile', 'README.md']) # => true FileUtils.uptodate?('Gemfile', ['Rakefile', 'README.md']) # => false
A non-existent file is considered to be infinitely old.
Related: FileUtils.touch
.