fileutils 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/Rakefile +1 -3
- data/fileutils.gemspec +1 -1
- data/lib/fileutils.rb +1332 -399
- metadata +4 -4
data/lib/fileutils.rb
CHANGED
|
@@ -6,103 +6,181 @@ rescue LoadError
|
|
|
6
6
|
# for make mjit-headers
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
# Namespace for file utility methods for copying, moving, removing, etc.
|
|
9
10
|
#
|
|
10
|
-
#
|
|
11
|
+
# == What's Here
|
|
11
12
|
#
|
|
12
|
-
#
|
|
13
|
+
# First, what’s elsewhere. \Module \FileUtils:
|
|
13
14
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
15
|
+
# - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html].
|
|
16
|
+
# - Supplements {class File}[https://docs.ruby-lang.org/en/master/File.html]
|
|
17
|
+
# (but is not included or extended there).
|
|
16
18
|
#
|
|
17
|
-
#
|
|
19
|
+
# Here, module \FileUtils provides methods that are useful for:
|
|
18
20
|
#
|
|
19
|
-
#
|
|
21
|
+
# - {Creating}[rdoc-ref:FileUtils@Creating].
|
|
22
|
+
# - {Deleting}[rdoc-ref:FileUtils@Deleting].
|
|
23
|
+
# - {Querying}[rdoc-ref:FileUtils@Querying].
|
|
24
|
+
# - {Setting}[rdoc-ref:FileUtils@Setting].
|
|
25
|
+
# - {Comparing}[rdoc-ref:FileUtils@Comparing].
|
|
26
|
+
# - {Copying}[rdoc-ref:FileUtils@Copying].
|
|
27
|
+
# - {Moving}[rdoc-ref:FileUtils@Moving].
|
|
28
|
+
# - {Options}[rdoc-ref:FileUtils@Options].
|
|
20
29
|
#
|
|
21
|
-
# ===
|
|
30
|
+
# === Creating
|
|
22
31
|
#
|
|
23
|
-
#
|
|
32
|
+
# - ::mkdir: Creates directories.
|
|
33
|
+
# - ::mkdir_p, ::makedirs, ::mkpath: Creates directories,
|
|
34
|
+
# also creating ancestor directories as needed.
|
|
35
|
+
# - ::link_entry: Creates a hard link.
|
|
36
|
+
# - ::ln, ::link: Creates hard links.
|
|
37
|
+
# - ::ln_s, ::symlink: Creates symbolic links.
|
|
38
|
+
# - ::ln_sf: Creates symbolic links, overwriting if necessary.
|
|
39
|
+
# - ::ln_sr: Creates symbolic links relative to targets
|
|
24
40
|
#
|
|
25
|
-
#
|
|
26
|
-
# FileUtils.cd(dir, **options) {|dir| block }
|
|
27
|
-
# FileUtils.pwd()
|
|
28
|
-
# FileUtils.mkdir(dir, **options)
|
|
29
|
-
# FileUtils.mkdir(list, **options)
|
|
30
|
-
# FileUtils.mkdir_p(dir, **options)
|
|
31
|
-
# FileUtils.mkdir_p(list, **options)
|
|
32
|
-
# FileUtils.rmdir(dir, **options)
|
|
33
|
-
# FileUtils.rmdir(list, **options)
|
|
34
|
-
# FileUtils.ln(target, link, **options)
|
|
35
|
-
# FileUtils.ln(targets, dir, **options)
|
|
36
|
-
# FileUtils.ln_s(target, link, **options)
|
|
37
|
-
# FileUtils.ln_s(targets, dir, **options)
|
|
38
|
-
# FileUtils.ln_sf(target, link, **options)
|
|
39
|
-
# FileUtils.cp(src, dest, **options)
|
|
40
|
-
# FileUtils.cp(list, dir, **options)
|
|
41
|
-
# FileUtils.cp_r(src, dest, **options)
|
|
42
|
-
# FileUtils.cp_r(list, dir, **options)
|
|
43
|
-
# FileUtils.mv(src, dest, **options)
|
|
44
|
-
# FileUtils.mv(list, dir, **options)
|
|
45
|
-
# FileUtils.rm(list, **options)
|
|
46
|
-
# FileUtils.rm_r(list, **options)
|
|
47
|
-
# FileUtils.rm_rf(list, **options)
|
|
48
|
-
# FileUtils.install(src, dest, **options)
|
|
49
|
-
# FileUtils.chmod(mode, list, **options)
|
|
50
|
-
# FileUtils.chmod_R(mode, list, **options)
|
|
51
|
-
# FileUtils.chown(user, group, list, **options)
|
|
52
|
-
# FileUtils.chown_R(user, group, list, **options)
|
|
53
|
-
# FileUtils.touch(list, **options)
|
|
41
|
+
# === Deleting
|
|
54
42
|
#
|
|
55
|
-
#
|
|
43
|
+
# - ::remove_dir: Removes a directory and its descendants.
|
|
44
|
+
# - ::remove_entry: Removes an entry, including its descendants if it is a directory.
|
|
45
|
+
# - ::remove_entry_secure: Like ::remove_entry, but removes securely.
|
|
46
|
+
# - ::remove_file: Removes a file entry.
|
|
47
|
+
# - ::rm, ::remove: Removes entries.
|
|
48
|
+
# - ::rm_f, ::safe_unlink: Like ::rm, but removes forcibly.
|
|
49
|
+
# - ::rm_r: Removes entries and their descendants.
|
|
50
|
+
# - ::rm_rf, ::rmtree: Like ::rm_r, but removes forcibly.
|
|
51
|
+
# - ::rmdir: Removes directories.
|
|
56
52
|
#
|
|
57
|
-
#
|
|
58
|
-
# directories if not empty, etc.);
|
|
59
|
-
# <tt>:verbose</tt> :: print command to be run, in bash syntax, before
|
|
60
|
-
# performing it;
|
|
61
|
-
# <tt>:preserve</tt> :: preserve object's group, user and modification
|
|
62
|
-
# time on copying;
|
|
63
|
-
# <tt>:noop</tt> :: no changes are made (usable in combination with
|
|
64
|
-
# <tt>:verbose</tt> which will print the command to run)
|
|
53
|
+
# === Querying
|
|
65
54
|
#
|
|
66
|
-
#
|
|
67
|
-
# ::
|
|
68
|
-
# options.
|
|
55
|
+
# - ::pwd, ::getwd: Returns the path to the working directory.
|
|
56
|
+
# - ::uptodate?: Returns whether a given entry is newer than given other entries.
|
|
69
57
|
#
|
|
70
|
-
#
|
|
71
|
-
# either one file or a list of files in that argument. See the method
|
|
72
|
-
# documentation for examples.
|
|
58
|
+
# === Setting
|
|
73
59
|
#
|
|
74
|
-
#
|
|
60
|
+
# - ::cd, ::chdir: Sets the working directory.
|
|
61
|
+
# - ::chmod: Sets permissions for an entry.
|
|
62
|
+
# - ::chmod_R: Sets permissions for an entry and its descendants.
|
|
63
|
+
# - ::chown: Sets the owner and group for entries.
|
|
64
|
+
# - ::chown_R: Sets the owner and group for entries and their descendants.
|
|
65
|
+
# - ::touch: Sets modification and access times for entries,
|
|
66
|
+
# creating if necessary.
|
|
75
67
|
#
|
|
76
|
-
#
|
|
77
|
-
# FileUtils.copy_file(src, dest, preserve = false, dereference = true)
|
|
78
|
-
# FileUtils.copy_stream(srcstream, deststream)
|
|
79
|
-
# FileUtils.remove_entry(path, force = false)
|
|
80
|
-
# FileUtils.remove_entry_secure(path, force = false)
|
|
81
|
-
# FileUtils.remove_file(path, force = false)
|
|
82
|
-
# FileUtils.compare_file(path_a, path_b)
|
|
83
|
-
# FileUtils.compare_stream(stream_a, stream_b)
|
|
84
|
-
# FileUtils.uptodate?(file, cmp_list)
|
|
68
|
+
# === Comparing
|
|
85
69
|
#
|
|
86
|
-
#
|
|
70
|
+
# - ::compare_file, ::cmp, ::identical?: Returns whether two entries are identical.
|
|
71
|
+
# - ::compare_stream: Returns whether two streams are identical.
|
|
87
72
|
#
|
|
88
|
-
#
|
|
89
|
-
# before acting. This equates to passing the <tt>:verbose</tt> flag to methods
|
|
90
|
-
# in FileUtils.
|
|
73
|
+
# === Copying
|
|
91
74
|
#
|
|
92
|
-
#
|
|
75
|
+
# - ::copy_entry: Recursively copies an entry.
|
|
76
|
+
# - ::copy_file: Copies an entry.
|
|
77
|
+
# - ::copy_stream: Copies a stream.
|
|
78
|
+
# - ::cp, ::copy: Copies files.
|
|
79
|
+
# - ::cp_lr: Recursively creates hard links.
|
|
80
|
+
# - ::cp_r: Recursively copies files, retaining mode, owner, and group.
|
|
81
|
+
# - ::install: Recursively copies files, optionally setting mode,
|
|
82
|
+
# owner, and group.
|
|
93
83
|
#
|
|
94
|
-
#
|
|
95
|
-
# files/directories. This equates to passing the <tt>:noop</tt> flag to methods
|
|
96
|
-
# in FileUtils.
|
|
84
|
+
# === Moving
|
|
97
85
|
#
|
|
98
|
-
#
|
|
86
|
+
# - ::mv, ::move: Moves entries.
|
|
99
87
|
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
88
|
+
# === Options
|
|
89
|
+
#
|
|
90
|
+
# - ::collect_method: Returns the names of methods that accept a given option.
|
|
91
|
+
# - ::commands: Returns the names of methods that accept options.
|
|
92
|
+
# - ::have_option?: Returns whether a given method accepts a given option.
|
|
93
|
+
# - ::options: Returns all option names.
|
|
94
|
+
# - ::options_of: Returns the names of the options for a given method.
|
|
95
|
+
#
|
|
96
|
+
# == Path Arguments
|
|
97
|
+
#
|
|
98
|
+
# Some methods in \FileUtils accept _path_ arguments,
|
|
99
|
+
# which are interpreted as paths to filesystem entries:
|
|
100
|
+
#
|
|
101
|
+
# - If the argument is a string, that value is the path.
|
|
102
|
+
# - If the argument has method +:to_path+, it is converted via that method.
|
|
103
|
+
# - If the argument has method +:to_str+, it is converted via that method.
|
|
104
|
+
#
|
|
105
|
+
# == About the Examples
|
|
106
|
+
#
|
|
107
|
+
# Some examples here involve trees of file entries.
|
|
108
|
+
# For these, we sometimes display trees using the
|
|
109
|
+
# {tree command-line utility}[https://en.wikipedia.org/wiki/Tree_(command)],
|
|
110
|
+
# which is a recursive directory-listing utility that produces
|
|
111
|
+
# a depth-indented listing of files and directories.
|
|
112
|
+
#
|
|
113
|
+
# We use a helper method to launch the command and control the format:
|
|
114
|
+
#
|
|
115
|
+
# def tree(dirpath = '.')
|
|
116
|
+
# command = "tree --noreport --charset=ascii #{dirpath}"
|
|
117
|
+
# system(command)
|
|
118
|
+
# end
|
|
119
|
+
#
|
|
120
|
+
# To illustrate:
|
|
121
|
+
#
|
|
122
|
+
# tree('src0')
|
|
123
|
+
# # => src0
|
|
124
|
+
# # |-- sub0
|
|
125
|
+
# # | |-- src0.txt
|
|
126
|
+
# # | `-- src1.txt
|
|
127
|
+
# # `-- sub1
|
|
128
|
+
# # |-- src2.txt
|
|
129
|
+
# # `-- src3.txt
|
|
130
|
+
#
|
|
131
|
+
# == Avoiding the TOCTTOU Vulnerability
|
|
132
|
+
#
|
|
133
|
+
# For certain methods that recursively remove entries,
|
|
134
|
+
# there is a potential vulnerability called the
|
|
135
|
+
# {Time-of-check to time-of-use}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use],
|
|
136
|
+
# or TOCTTOU, vulnerability that can exist when:
|
|
137
|
+
#
|
|
138
|
+
# - An ancestor directory of the entry at the target path is world writable;
|
|
139
|
+
# such directories include <tt>/tmp</tt>.
|
|
140
|
+
# - The directory tree at the target path includes:
|
|
141
|
+
#
|
|
142
|
+
# - A world-writable descendant directory.
|
|
143
|
+
# - A symbolic link.
|
|
144
|
+
#
|
|
145
|
+
# To avoid that vulnerability, you can use this method to remove entries:
|
|
146
|
+
#
|
|
147
|
+
# - FileUtils.remove_entry_secure: removes recursively
|
|
148
|
+
# if the target path points to a directory.
|
|
149
|
+
#
|
|
150
|
+
# Also available are these methods,
|
|
151
|
+
# each of which calls \FileUtils.remove_entry_secure:
|
|
152
|
+
#
|
|
153
|
+
# - FileUtils.rm_r with keyword argument <tt>secure: true</tt>.
|
|
154
|
+
# - FileUtils.rm_rf with keyword argument <tt>secure: true</tt>.
|
|
155
|
+
#
|
|
156
|
+
# Finally, this method for moving entries calls \FileUtils.remove_entry_secure
|
|
157
|
+
# if the source and destination are on different file systems
|
|
158
|
+
# (which means that the "move" is really a copy and remove):
|
|
159
|
+
#
|
|
160
|
+
# - FileUtils.mv with keyword argument <tt>secure: true</tt>.
|
|
161
|
+
#
|
|
162
|
+
# \Method \FileUtils.remove_entry_secure removes securely
|
|
163
|
+
# by applying a special pre-process:
|
|
164
|
+
#
|
|
165
|
+
# - If the target path points to a directory, this method uses methods
|
|
166
|
+
# {File#chown}[https://docs.ruby-lang.org/en/master/File.html#method-i-chown]
|
|
167
|
+
# and {File#chmod}[https://docs.ruby-lang.org/en/master/File.html#method-i-chmod]
|
|
168
|
+
# in removing directories.
|
|
169
|
+
# - The owner of the target directory should be either the current process
|
|
170
|
+
# or the super user (root).
|
|
171
|
+
#
|
|
172
|
+
# WARNING: You must ensure that *ALL* parent directories cannot be
|
|
173
|
+
# moved by other untrusted users. For example, parent directories
|
|
174
|
+
# should not be owned by untrusted users, and should not be world
|
|
175
|
+
# writable except when the sticky bit is set.
|
|
176
|
+
#
|
|
177
|
+
# For details of this security vulnerability, see Perl cases:
|
|
178
|
+
#
|
|
179
|
+
# - {CVE-2005-0448}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448].
|
|
180
|
+
# - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452].
|
|
103
181
|
#
|
|
104
182
|
module FileUtils
|
|
105
|
-
VERSION = "1.
|
|
183
|
+
VERSION = "1.7.0"
|
|
106
184
|
|
|
107
185
|
def self.private_module_function(name) #:nodoc:
|
|
108
186
|
module_function name
|
|
@@ -110,7 +188,13 @@ module FileUtils
|
|
|
110
188
|
end
|
|
111
189
|
|
|
112
190
|
#
|
|
113
|
-
# Returns the
|
|
191
|
+
# Returns a string containing the path to the current directory:
|
|
192
|
+
#
|
|
193
|
+
# FileUtils.pwd # => "/rdoc/fileutils"
|
|
194
|
+
#
|
|
195
|
+
# FileUtils.getwd is an alias for FileUtils.pwd.
|
|
196
|
+
#
|
|
197
|
+
# Related: FileUtils.cd.
|
|
114
198
|
#
|
|
115
199
|
def pwd
|
|
116
200
|
Dir.pwd
|
|
@@ -120,19 +204,40 @@ module FileUtils
|
|
|
120
204
|
alias getwd pwd
|
|
121
205
|
module_function :getwd
|
|
122
206
|
|
|
207
|
+
# Changes the working directory to the given +dir+, which
|
|
208
|
+
# should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments]:
|
|
209
|
+
#
|
|
210
|
+
# With no block given,
|
|
211
|
+
# changes the current directory to the directory at +dir+; returns zero:
|
|
212
|
+
#
|
|
213
|
+
# FileUtils.pwd # => "/rdoc/fileutils"
|
|
214
|
+
# FileUtils.cd('..')
|
|
215
|
+
# FileUtils.pwd # => "/rdoc"
|
|
216
|
+
# FileUtils.cd('fileutils')
|
|
217
|
+
#
|
|
218
|
+
# With a block given, changes the current directory to the directory
|
|
219
|
+
# at +dir+, calls the block with argument +dir+,
|
|
220
|
+
# and restores the original current directory; returns the block's value:
|
|
221
|
+
#
|
|
222
|
+
# FileUtils.pwd # => "/rdoc/fileutils"
|
|
223
|
+
# FileUtils.cd('..') { |arg| [arg, FileUtils.pwd] } # => ["..", "/rdoc"]
|
|
224
|
+
# FileUtils.pwd # => "/rdoc/fileutils"
|
|
225
|
+
#
|
|
226
|
+
# Keyword arguments:
|
|
227
|
+
#
|
|
228
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
123
229
|
#
|
|
124
|
-
#
|
|
230
|
+
# FileUtils.cd('..')
|
|
231
|
+
# FileUtils.cd('fileutils')
|
|
125
232
|
#
|
|
126
|
-
#
|
|
127
|
-
# working directory after the block execution has finished.
|
|
233
|
+
# Output:
|
|
128
234
|
#
|
|
129
|
-
#
|
|
235
|
+
# cd ..
|
|
236
|
+
# cd fileutils
|
|
130
237
|
#
|
|
131
|
-
#
|
|
238
|
+
# FileUtils.chdir is an alias for FileUtils.cd.
|
|
132
239
|
#
|
|
133
|
-
#
|
|
134
|
-
# # ... # do something
|
|
135
|
-
# end # return to original directory
|
|
240
|
+
# Related: FileUtils.pwd.
|
|
136
241
|
#
|
|
137
242
|
def cd(dir, verbose: nil, &block) # :yield: dir
|
|
138
243
|
fu_output_message "cd #{dir}" if verbose
|
|
@@ -146,11 +251,19 @@ module FileUtils
|
|
|
146
251
|
module_function :chdir
|
|
147
252
|
|
|
148
253
|
#
|
|
149
|
-
# Returns true if
|
|
150
|
-
#
|
|
254
|
+
# Returns +true+ if the file at path +new+
|
|
255
|
+
# is newer than all the files at paths in array +old_list+;
|
|
256
|
+
# +false+ otherwise.
|
|
151
257
|
#
|
|
152
|
-
#
|
|
153
|
-
#
|
|
258
|
+
# Argument +new+ and the elements of +old_list+
|
|
259
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments]:
|
|
260
|
+
#
|
|
261
|
+
# FileUtils.uptodate?('Rakefile', ['Gemfile', 'README.md']) # => true
|
|
262
|
+
# FileUtils.uptodate?('Gemfile', ['Rakefile', 'README.md']) # => false
|
|
263
|
+
#
|
|
264
|
+
# A non-existent file is considered to be infinitely old.
|
|
265
|
+
#
|
|
266
|
+
# Related: FileUtils.touch.
|
|
154
267
|
#
|
|
155
268
|
def uptodate?(new, old_list)
|
|
156
269
|
return false unless File.exist?(new)
|
|
@@ -170,12 +283,39 @@ module FileUtils
|
|
|
170
283
|
private_module_function :remove_trailing_slash
|
|
171
284
|
|
|
172
285
|
#
|
|
173
|
-
# Creates
|
|
286
|
+
# Creates directories at the paths in the given +list+
|
|
287
|
+
# (a single path or an array of paths);
|
|
288
|
+
# returns +list+ if it is an array, <tt>[list]</tt> otherwise.
|
|
289
|
+
#
|
|
290
|
+
# Argument +list+ or its elements
|
|
291
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
292
|
+
#
|
|
293
|
+
# With no keyword arguments, creates a directory at each +path+ in +list+
|
|
294
|
+
# by calling: <tt>Dir.mkdir(path, mode)</tt>;
|
|
295
|
+
# see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
|
|
296
|
+
#
|
|
297
|
+
# FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"]
|
|
298
|
+
# FileUtils.mkdir('tmp4') # => ["tmp4"]
|
|
299
|
+
#
|
|
300
|
+
# Keyword arguments:
|
|
301
|
+
#
|
|
302
|
+
# - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
|
|
303
|
+
# see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
|
|
304
|
+
# - <tt>noop: true</tt> - does not create directories.
|
|
305
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
306
|
+
#
|
|
307
|
+
# FileUtils.mkdir(%w[tmp0 tmp1], verbose: true)
|
|
308
|
+
# FileUtils.mkdir(%w[tmp2 tmp3], mode: 0700, verbose: true)
|
|
174
309
|
#
|
|
175
|
-
#
|
|
176
|
-
#
|
|
177
|
-
#
|
|
178
|
-
#
|
|
310
|
+
# Output:
|
|
311
|
+
#
|
|
312
|
+
# mkdir tmp0 tmp1
|
|
313
|
+
# mkdir -m 700 tmp2 tmp3
|
|
314
|
+
#
|
|
315
|
+
# Raises an exception if any path points to an existing
|
|
316
|
+
# file or directory, or if for any reason a directory cannot be created.
|
|
317
|
+
#
|
|
318
|
+
# Related: FileUtils.mkdir_p.
|
|
179
319
|
#
|
|
180
320
|
def mkdir(list, mode: nil, noop: nil, verbose: nil)
|
|
181
321
|
list = fu_list(list)
|
|
@@ -189,19 +329,42 @@ module FileUtils
|
|
|
189
329
|
module_function :mkdir
|
|
190
330
|
|
|
191
331
|
#
|
|
192
|
-
# Creates
|
|
193
|
-
#
|
|
332
|
+
# Creates directories at the paths in the given +list+
|
|
333
|
+
# (a single path or an array of paths),
|
|
334
|
+
# also creating ancestor directories as needed;
|
|
335
|
+
# returns +list+ if it is an array, <tt>[list]</tt> otherwise.
|
|
336
|
+
#
|
|
337
|
+
# Argument +list+ or its elements
|
|
338
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
339
|
+
#
|
|
340
|
+
# With no keyword arguments, creates a directory at each +path+ in +list+,
|
|
341
|
+
# along with any needed ancestor directories,
|
|
342
|
+
# by calling: <tt>Dir.mkdir(path, mode)</tt>;
|
|
343
|
+
# see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
|
|
344
|
+
#
|
|
345
|
+
# FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
|
|
346
|
+
# FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"]
|
|
347
|
+
#
|
|
348
|
+
# Keyword arguments:
|
|
349
|
+
#
|
|
350
|
+
# - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
|
|
351
|
+
# see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
|
|
352
|
+
# - <tt>noop: true</tt> - does not create directories.
|
|
353
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
194
354
|
#
|
|
195
|
-
#
|
|
355
|
+
# FileUtils.mkdir_p(%w[tmp0 tmp1], verbose: true)
|
|
356
|
+
# FileUtils.mkdir_p(%w[tmp2 tmp3], mode: 0700, verbose: true)
|
|
196
357
|
#
|
|
197
|
-
#
|
|
358
|
+
# Output:
|
|
198
359
|
#
|
|
199
|
-
#
|
|
200
|
-
#
|
|
201
|
-
# * /usr/local/lib
|
|
202
|
-
# * /usr/local/lib/ruby
|
|
360
|
+
# mkdir -p tmp0 tmp1
|
|
361
|
+
# mkdir -p -m 700 tmp2 tmp3
|
|
203
362
|
#
|
|
204
|
-
#
|
|
363
|
+
# Raises an exception if for any reason a directory cannot be created.
|
|
364
|
+
#
|
|
365
|
+
# FileUtils.mkpath and FileUtils.makedirs are aliases for FileUtils.mkdir_p.
|
|
366
|
+
#
|
|
367
|
+
# Related: FileUtils.mkdir.
|
|
205
368
|
#
|
|
206
369
|
def mkdir_p(list, mode: nil, noop: nil, verbose: nil)
|
|
207
370
|
list = fu_list(list)
|
|
@@ -211,21 +374,11 @@ module FileUtils
|
|
|
211
374
|
list.each do |item|
|
|
212
375
|
path = remove_trailing_slash(item)
|
|
213
376
|
|
|
214
|
-
# optimize for the most common case
|
|
215
|
-
begin
|
|
216
|
-
fu_mkdir path, mode
|
|
217
|
-
next
|
|
218
|
-
rescue SystemCallError
|
|
219
|
-
next if File.directory?(path)
|
|
220
|
-
end
|
|
221
|
-
|
|
222
377
|
stack = []
|
|
223
|
-
until path
|
|
378
|
+
until File.directory?(path) || File.dirname(path) == path
|
|
224
379
|
stack.push path
|
|
225
380
|
path = File.dirname(path)
|
|
226
|
-
break if File.directory?(path)
|
|
227
381
|
end
|
|
228
|
-
stack.pop if path == stack.last # root directory should exist
|
|
229
382
|
stack.reverse_each do |dir|
|
|
230
383
|
begin
|
|
231
384
|
fu_mkdir dir, mode
|
|
@@ -256,12 +409,39 @@ module FileUtils
|
|
|
256
409
|
private_module_function :fu_mkdir
|
|
257
410
|
|
|
258
411
|
#
|
|
259
|
-
# Removes
|
|
412
|
+
# Removes directories at the paths in the given +list+
|
|
413
|
+
# (a single path or an array of paths);
|
|
414
|
+
# returns +list+, if it is an array, <tt>[list]</tt> otherwise.
|
|
415
|
+
#
|
|
416
|
+
# Argument +list+ or its elements
|
|
417
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
418
|
+
#
|
|
419
|
+
# With no keyword arguments, removes the directory at each +path+ in +list+,
|
|
420
|
+
# by calling: <tt>Dir.rmdir(path)</tt>;
|
|
421
|
+
# see {Dir.rmdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-rmdir]:
|
|
422
|
+
#
|
|
423
|
+
# FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
|
|
424
|
+
# FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"]
|
|
260
425
|
#
|
|
261
|
-
#
|
|
262
|
-
#
|
|
263
|
-
#
|
|
264
|
-
#
|
|
426
|
+
# Keyword arguments:
|
|
427
|
+
#
|
|
428
|
+
# - <tt>parents: true</tt> - removes successive ancestor directories
|
|
429
|
+
# if empty.
|
|
430
|
+
# - <tt>noop: true</tt> - does not remove directories.
|
|
431
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
432
|
+
#
|
|
433
|
+
# FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3], parents: true, verbose: true)
|
|
434
|
+
# FileUtils.rmdir('tmp4/tmp5', parents: true, verbose: true)
|
|
435
|
+
#
|
|
436
|
+
# Output:
|
|
437
|
+
#
|
|
438
|
+
# rmdir -p tmp0/tmp1 tmp2/tmp3
|
|
439
|
+
# rmdir -p tmp4/tmp5
|
|
440
|
+
#
|
|
441
|
+
# Raises an exception if a directory does not exist
|
|
442
|
+
# or if for any reason a directory cannot be removed.
|
|
443
|
+
#
|
|
444
|
+
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
|
|
265
445
|
#
|
|
266
446
|
def rmdir(list, parents: nil, noop: nil, verbose: nil)
|
|
267
447
|
list = fu_list(list)
|
|
@@ -282,26 +462,62 @@ module FileUtils
|
|
|
282
462
|
end
|
|
283
463
|
module_function :rmdir
|
|
284
464
|
|
|
465
|
+
# Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
|
|
466
|
+
#
|
|
467
|
+
# Arguments +src+ (a single path or an array of paths)
|
|
468
|
+
# and +dest+ (a single path)
|
|
469
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
470
|
+
#
|
|
471
|
+
# When +src+ is the path to an existing file
|
|
472
|
+
# and +dest+ is the path to a non-existent file,
|
|
473
|
+
# creates a hard link at +dest+ pointing to +src+; returns zero:
|
|
285
474
|
#
|
|
286
|
-
#
|
|
287
|
-
#
|
|
288
|
-
# FileUtils.ln(
|
|
289
|
-
#
|
|
475
|
+
# Dir.children('tmp0/') # => ["t.txt"]
|
|
476
|
+
# Dir.children('tmp1/') # => []
|
|
477
|
+
# FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk') # => 0
|
|
478
|
+
# Dir.children('tmp1/') # => ["t.lnk"]
|
|
290
479
|
#
|
|
291
|
-
#
|
|
292
|
-
#
|
|
293
|
-
#
|
|
480
|
+
# When +src+ is the path to an existing file
|
|
481
|
+
# and +dest+ is the path to an existing directory,
|
|
482
|
+
# creates a hard link at <tt>dest/src</tt> pointing to +src+; returns zero:
|
|
294
483
|
#
|
|
295
|
-
#
|
|
296
|
-
#
|
|
484
|
+
# Dir.children('tmp2') # => ["t.dat"]
|
|
485
|
+
# Dir.children('tmp3') # => []
|
|
486
|
+
# FileUtils.ln('tmp2/t.dat', 'tmp3') # => 0
|
|
487
|
+
# Dir.children('tmp3') # => ["t.dat"]
|
|
297
488
|
#
|
|
298
|
-
#
|
|
299
|
-
#
|
|
300
|
-
#
|
|
301
|
-
#
|
|
489
|
+
# When +src+ is an array of paths to existing files
|
|
490
|
+
# and +dest+ is the path to an existing directory,
|
|
491
|
+
# then for each path +target+ in +src+,
|
|
492
|
+
# creates a hard link at <tt>dest/target</tt> pointing to +target+;
|
|
493
|
+
# returns +src+:
|
|
302
494
|
#
|
|
303
|
-
#
|
|
304
|
-
# FileUtils.ln
|
|
495
|
+
# Dir.children('tmp4/') # => []
|
|
496
|
+
# FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/') # => ["tmp0/t.txt", "tmp2/t.dat"]
|
|
497
|
+
# Dir.children('tmp4/') # => ["t.dat", "t.txt"]
|
|
498
|
+
#
|
|
499
|
+
# Keyword arguments:
|
|
500
|
+
#
|
|
501
|
+
# - <tt>force: true</tt> - overwrites +dest+ if it exists.
|
|
502
|
+
# - <tt>noop: true</tt> - does not create links.
|
|
503
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
504
|
+
#
|
|
505
|
+
# FileUtils.ln('tmp0/t.txt', 'tmp1/t.lnk', verbose: true)
|
|
506
|
+
# FileUtils.ln('tmp2/t.dat', 'tmp3', verbose: true)
|
|
507
|
+
# FileUtils.ln(['tmp0/t.txt', 'tmp2/t.dat'], 'tmp4/', verbose: true)
|
|
508
|
+
#
|
|
509
|
+
# Output:
|
|
510
|
+
#
|
|
511
|
+
# ln tmp0/t.txt tmp1/t.lnk
|
|
512
|
+
# ln tmp2/t.dat tmp3
|
|
513
|
+
# ln tmp0/t.txt tmp2/t.dat tmp4/
|
|
514
|
+
#
|
|
515
|
+
# Raises an exception if +dest+ is the path to an existing file
|
|
516
|
+
# and keyword argument +force+ is not +true+.
|
|
517
|
+
#
|
|
518
|
+
# FileUtils#link is an alias for FileUtils#ln.
|
|
519
|
+
#
|
|
520
|
+
# Related: FileUtils.link_entry (has different options).
|
|
305
521
|
#
|
|
306
522
|
def ln(src, dest, force: nil, noop: nil, verbose: nil)
|
|
307
523
|
fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
|
|
@@ -316,28 +532,103 @@ module FileUtils
|
|
|
316
532
|
alias link ln
|
|
317
533
|
module_function :link
|
|
318
534
|
|
|
319
|
-
#
|
|
320
|
-
#
|
|
321
|
-
#
|
|
322
|
-
# +
|
|
323
|
-
#
|
|
324
|
-
#
|
|
325
|
-
#
|
|
326
|
-
#
|
|
327
|
-
#
|
|
328
|
-
#
|
|
329
|
-
#
|
|
330
|
-
#
|
|
331
|
-
#
|
|
332
|
-
#
|
|
333
|
-
# #
|
|
334
|
-
#
|
|
335
|
-
#
|
|
336
|
-
#
|
|
337
|
-
#
|
|
338
|
-
#
|
|
339
|
-
# #
|
|
340
|
-
#
|
|
535
|
+
# Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link].
|
|
536
|
+
#
|
|
537
|
+
# Arguments +src+ (a single path or an array of paths)
|
|
538
|
+
# and +dest+ (a single path)
|
|
539
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
540
|
+
#
|
|
541
|
+
# If +src+ is the path to a directory and +dest+ does not exist,
|
|
542
|
+
# creates links +dest+ and descendents pointing to +src+ and its descendents:
|
|
543
|
+
#
|
|
544
|
+
# tree('src0')
|
|
545
|
+
# # => src0
|
|
546
|
+
# # |-- sub0
|
|
547
|
+
# # | |-- src0.txt
|
|
548
|
+
# # | `-- src1.txt
|
|
549
|
+
# # `-- sub1
|
|
550
|
+
# # |-- src2.txt
|
|
551
|
+
# # `-- src3.txt
|
|
552
|
+
# File.exist?('dest0') # => false
|
|
553
|
+
# FileUtils.cp_lr('src0', 'dest0')
|
|
554
|
+
# tree('dest0')
|
|
555
|
+
# # => dest0
|
|
556
|
+
# # |-- sub0
|
|
557
|
+
# # | |-- src0.txt
|
|
558
|
+
# # | `-- src1.txt
|
|
559
|
+
# # `-- sub1
|
|
560
|
+
# # |-- src2.txt
|
|
561
|
+
# # `-- src3.txt
|
|
562
|
+
#
|
|
563
|
+
# If +src+ and +dest+ are both paths to directories,
|
|
564
|
+
# creates links <tt>dest/src</tt> and descendents
|
|
565
|
+
# pointing to +src+ and its descendents:
|
|
566
|
+
#
|
|
567
|
+
# tree('src1')
|
|
568
|
+
# # => src1
|
|
569
|
+
# # |-- sub0
|
|
570
|
+
# # | |-- src0.txt
|
|
571
|
+
# # | `-- src1.txt
|
|
572
|
+
# # `-- sub1
|
|
573
|
+
# # |-- src2.txt
|
|
574
|
+
# # `-- src3.txt
|
|
575
|
+
# FileUtils.mkdir('dest1')
|
|
576
|
+
# FileUtils.cp_lr('src1', 'dest1')
|
|
577
|
+
# tree('dest1')
|
|
578
|
+
# # => dest1
|
|
579
|
+
# # `-- src1
|
|
580
|
+
# # |-- sub0
|
|
581
|
+
# # | |-- src0.txt
|
|
582
|
+
# # | `-- src1.txt
|
|
583
|
+
# # `-- sub1
|
|
584
|
+
# # |-- src2.txt
|
|
585
|
+
# # `-- src3.txt
|
|
586
|
+
#
|
|
587
|
+
# If +src+ is an array of paths to entries and +dest+ is the path to a directory,
|
|
588
|
+
# for each path +filepath+ in +src+, creates a link at <tt>dest/filepath</tt>
|
|
589
|
+
# pointing to that path:
|
|
590
|
+
#
|
|
591
|
+
# tree('src2')
|
|
592
|
+
# # => src2
|
|
593
|
+
# # |-- sub0
|
|
594
|
+
# # | |-- src0.txt
|
|
595
|
+
# # | `-- src1.txt
|
|
596
|
+
# # `-- sub1
|
|
597
|
+
# # |-- src2.txt
|
|
598
|
+
# # `-- src3.txt
|
|
599
|
+
# FileUtils.mkdir('dest2')
|
|
600
|
+
# FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2')
|
|
601
|
+
# tree('dest2')
|
|
602
|
+
# # => dest2
|
|
603
|
+
# # |-- sub0
|
|
604
|
+
# # | |-- src0.txt
|
|
605
|
+
# # | `-- src1.txt
|
|
606
|
+
# # `-- sub1
|
|
607
|
+
# # |-- src2.txt
|
|
608
|
+
# # `-- src3.txt
|
|
609
|
+
#
|
|
610
|
+
# Keyword arguments:
|
|
611
|
+
#
|
|
612
|
+
# - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
|
|
613
|
+
# does not dereference it.
|
|
614
|
+
# - <tt>noop: true</tt> - does not create links.
|
|
615
|
+
# - <tt>remove_destination: true</tt> - removes +dest+ before creating links.
|
|
616
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
617
|
+
#
|
|
618
|
+
# FileUtils.cp_lr('src0', 'dest0', noop: true, verbose: true)
|
|
619
|
+
# FileUtils.cp_lr('src1', 'dest1', noop: true, verbose: true)
|
|
620
|
+
# FileUtils.cp_lr(['src2/sub0', 'src2/sub1'], 'dest2', noop: true, verbose: true)
|
|
621
|
+
#
|
|
622
|
+
# Output:
|
|
623
|
+
#
|
|
624
|
+
# cp -lr src0 dest0
|
|
625
|
+
# cp -lr src1 dest1
|
|
626
|
+
# cp -lr src2/sub0 src2/sub1 dest2
|
|
627
|
+
#
|
|
628
|
+
# Raises an exception if +dest+ is the path to an existing file or directory
|
|
629
|
+
# and keyword argument <tt>remove_destination: true</tt> is not given.
|
|
630
|
+
#
|
|
631
|
+
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
|
|
341
632
|
#
|
|
342
633
|
def cp_lr(src, dest, noop: nil, verbose: nil,
|
|
343
634
|
dereference_root: true, remove_destination: false)
|
|
@@ -349,27 +640,81 @@ module FileUtils
|
|
|
349
640
|
end
|
|
350
641
|
module_function :cp_lr
|
|
351
642
|
|
|
643
|
+
# Creates {symbolic links}[https://en.wikipedia.org/wiki/Symbolic_link].
|
|
644
|
+
#
|
|
645
|
+
# Arguments +src+ (a single path or an array of paths)
|
|
646
|
+
# and +dest+ (a single path)
|
|
647
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
648
|
+
#
|
|
649
|
+
# If +src+ is the path to an existing file:
|
|
352
650
|
#
|
|
353
|
-
#
|
|
354
|
-
#
|
|
355
|
-
# FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil)
|
|
356
|
-
# FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil)
|
|
651
|
+
# - When +dest+ is the path to a non-existent file,
|
|
652
|
+
# creates a symbolic link at +dest+ pointing to +src+:
|
|
357
653
|
#
|
|
358
|
-
#
|
|
359
|
-
#
|
|
360
|
-
#
|
|
654
|
+
# FileUtils.touch('src0.txt')
|
|
655
|
+
# File.exist?('dest0.txt') # => false
|
|
656
|
+
# FileUtils.ln_s('src0.txt', 'dest0.txt')
|
|
657
|
+
# File.symlink?('dest0.txt') # => true
|
|
361
658
|
#
|
|
362
|
-
#
|
|
363
|
-
#
|
|
659
|
+
# - When +dest+ is the path to an existing file,
|
|
660
|
+
# creates a symbolic link at +dest+ pointing to +src+
|
|
661
|
+
# if and only if keyword argument <tt>force: true</tt> is given
|
|
662
|
+
# (raises an exception otherwise):
|
|
364
663
|
#
|
|
365
|
-
#
|
|
366
|
-
#
|
|
367
|
-
#
|
|
368
|
-
#
|
|
664
|
+
# FileUtils.touch('src1.txt')
|
|
665
|
+
# FileUtils.touch('dest1.txt')
|
|
666
|
+
# FileUtils.ln_s('src1.txt', 'dest1.txt', force: true)
|
|
667
|
+
# FileTest.symlink?('dest1.txt') # => true
|
|
369
668
|
#
|
|
370
|
-
#
|
|
669
|
+
# FileUtils.ln_s('src1.txt', 'dest1.txt') # Raises Errno::EEXIST.
|
|
371
670
|
#
|
|
372
|
-
|
|
671
|
+
# If +dest+ is the path to a directory,
|
|
672
|
+
# creates a symbolic link at <tt>dest/src</tt> pointing to +src+:
|
|
673
|
+
#
|
|
674
|
+
# FileUtils.touch('src2.txt')
|
|
675
|
+
# FileUtils.mkdir('destdir2')
|
|
676
|
+
# FileUtils.ln_s('src2.txt', 'destdir2')
|
|
677
|
+
# File.symlink?('destdir2/src2.txt') # => true
|
|
678
|
+
#
|
|
679
|
+
# If +src+ is an array of paths to existing files and +dest+ is a directory,
|
|
680
|
+
# for each child +child+ in +src+ creates a symbolic link <tt>dest/child</tt>
|
|
681
|
+
# pointing to +child+:
|
|
682
|
+
#
|
|
683
|
+
# FileUtils.mkdir('srcdir3')
|
|
684
|
+
# FileUtils.touch('srcdir3/src0.txt')
|
|
685
|
+
# FileUtils.touch('srcdir3/src1.txt')
|
|
686
|
+
# FileUtils.mkdir('destdir3')
|
|
687
|
+
# FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3')
|
|
688
|
+
# File.symlink?('destdir3/src0.txt') # => true
|
|
689
|
+
# File.symlink?('destdir3/src1.txt') # => true
|
|
690
|
+
#
|
|
691
|
+
# Keyword arguments:
|
|
692
|
+
#
|
|
693
|
+
# - <tt>force: true</tt> - overwrites +dest+ if it exists.
|
|
694
|
+
# - <tt>relative: false</tt> - create links relative to +dest+.
|
|
695
|
+
# - <tt>noop: true</tt> - does not create links.
|
|
696
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
697
|
+
#
|
|
698
|
+
# FileUtils.ln_s('src0.txt', 'dest0.txt', noop: true, verbose: true)
|
|
699
|
+
# FileUtils.ln_s('src1.txt', 'destdir1', noop: true, verbose: true)
|
|
700
|
+
# FileUtils.ln_s('src2.txt', 'dest2.txt', force: true, noop: true, verbose: true)
|
|
701
|
+
# FileUtils.ln_s(['srcdir3/src0.txt', 'srcdir3/src1.txt'], 'destdir3', noop: true, verbose: true)
|
|
702
|
+
#
|
|
703
|
+
# Output:
|
|
704
|
+
#
|
|
705
|
+
# ln -s src0.txt dest0.txt
|
|
706
|
+
# ln -s src1.txt destdir1
|
|
707
|
+
# ln -sf src2.txt dest2.txt
|
|
708
|
+
# ln -s srcdir3/src0.txt srcdir3/src1.txt destdir3
|
|
709
|
+
#
|
|
710
|
+
# FileUtils.symlink is an alias for FileUtils.ln_s.
|
|
711
|
+
#
|
|
712
|
+
# Related: FileUtils.ln_sf.
|
|
713
|
+
#
|
|
714
|
+
def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
|
|
715
|
+
if relative
|
|
716
|
+
return ln_sr(src, dest, force: force, noop: noop, verbose: verbose)
|
|
717
|
+
end
|
|
373
718
|
fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
|
|
374
719
|
return if noop
|
|
375
720
|
fu_each_src_dest0(src, dest) do |s,d|
|
|
@@ -382,29 +727,95 @@ module FileUtils
|
|
|
382
727
|
alias symlink ln_s
|
|
383
728
|
module_function :symlink
|
|
384
729
|
|
|
385
|
-
#
|
|
386
|
-
# :call-seq:
|
|
387
|
-
# FileUtils.ln_sf(*args)
|
|
388
|
-
#
|
|
389
|
-
# Same as
|
|
390
|
-
#
|
|
391
|
-
# FileUtils.ln_s(*args, force: true)
|
|
730
|
+
# Like FileUtils.ln_s, but always with keyword argument <tt>force: true</tt> given.
|
|
392
731
|
#
|
|
393
732
|
def ln_sf(src, dest, noop: nil, verbose: nil)
|
|
394
733
|
ln_s src, dest, force: true, noop: noop, verbose: verbose
|
|
395
734
|
end
|
|
396
735
|
module_function :ln_sf
|
|
397
736
|
|
|
737
|
+
# Like FileUtils.ln_s, but create links relative to +dest+.
|
|
398
738
|
#
|
|
399
|
-
|
|
400
|
-
|
|
739
|
+
def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
|
|
740
|
+
options = "#{force ? 'f' : ''}#{target_directory ? '' : 'T'}"
|
|
741
|
+
dest = File.path(dest)
|
|
742
|
+
srcs = Array(src)
|
|
743
|
+
link = proc do |s, target_dir_p = true|
|
|
744
|
+
s = File.path(s)
|
|
745
|
+
if target_dir_p
|
|
746
|
+
d = File.join(destdirs = dest, File.basename(s))
|
|
747
|
+
else
|
|
748
|
+
destdirs = File.dirname(d = dest)
|
|
749
|
+
end
|
|
750
|
+
destdirs = fu_split_path(File.realpath(destdirs))
|
|
751
|
+
if fu_starting_path?(s)
|
|
752
|
+
srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
|
|
753
|
+
base = fu_relative_components_from(srcdirs, destdirs)
|
|
754
|
+
s = File.join(*base)
|
|
755
|
+
else
|
|
756
|
+
srcdirs = fu_clean_components(*fu_split_path(s))
|
|
757
|
+
base = fu_relative_components_from(fu_split_path(Dir.pwd), destdirs)
|
|
758
|
+
while srcdirs.first&. == ".." and base.last&.!=("..") and !fu_starting_path?(base.last)
|
|
759
|
+
srcdirs.shift
|
|
760
|
+
base.pop
|
|
761
|
+
end
|
|
762
|
+
s = File.join(*base, *srcdirs)
|
|
763
|
+
end
|
|
764
|
+
fu_output_message "ln -s#{options} #{s} #{d}" if verbose
|
|
765
|
+
next if noop
|
|
766
|
+
remove_file d, true if force
|
|
767
|
+
File.symlink s, d
|
|
768
|
+
end
|
|
769
|
+
case srcs.size
|
|
770
|
+
when 0
|
|
771
|
+
when 1
|
|
772
|
+
link[srcs[0], target_directory && File.directory?(dest)]
|
|
773
|
+
else
|
|
774
|
+
srcs.each(&link)
|
|
775
|
+
end
|
|
776
|
+
end
|
|
777
|
+
module_function :ln_sr
|
|
778
|
+
|
|
779
|
+
# Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link]; returns +nil+.
|
|
780
|
+
#
|
|
781
|
+
# Arguments +src+ and +dest+
|
|
782
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
783
|
+
#
|
|
784
|
+
# If +src+ is the path to a file and +dest+ does not exist,
|
|
785
|
+
# creates a hard link at +dest+ pointing to +src+:
|
|
786
|
+
#
|
|
787
|
+
# FileUtils.touch('src0.txt')
|
|
788
|
+
# File.exist?('dest0.txt') # => false
|
|
789
|
+
# FileUtils.link_entry('src0.txt', 'dest0.txt')
|
|
790
|
+
# File.file?('dest0.txt') # => true
|
|
401
791
|
#
|
|
402
|
-
#
|
|
403
|
-
#
|
|
792
|
+
# If +src+ is the path to a directory and +dest+ does not exist,
|
|
793
|
+
# recursively creates hard links at +dest+ pointing to paths in +src+:
|
|
404
794
|
#
|
|
405
|
-
#
|
|
795
|
+
# FileUtils.mkdir_p(['src1/dir0', 'src1/dir1'])
|
|
796
|
+
# src_file_paths = [
|
|
797
|
+
# 'src1/dir0/t0.txt',
|
|
798
|
+
# 'src1/dir0/t1.txt',
|
|
799
|
+
# 'src1/dir1/t2.txt',
|
|
800
|
+
# 'src1/dir1/t3.txt',
|
|
801
|
+
# ]
|
|
802
|
+
# FileUtils.touch(src_file_paths)
|
|
803
|
+
# File.directory?('dest1') # => true
|
|
804
|
+
# FileUtils.link_entry('src1', 'dest1')
|
|
805
|
+
# File.file?('dest1/dir0/t0.txt') # => true
|
|
806
|
+
# File.file?('dest1/dir0/t1.txt') # => true
|
|
807
|
+
# File.file?('dest1/dir1/t2.txt') # => true
|
|
808
|
+
# File.file?('dest1/dir1/t3.txt') # => true
|
|
406
809
|
#
|
|
407
|
-
#
|
|
810
|
+
# Keyword arguments:
|
|
811
|
+
#
|
|
812
|
+
# - <tt>dereference_root: true</tt> - dereferences +src+ if it is a symbolic link.
|
|
813
|
+
# - <tt>remove_destination: true</tt> - removes +dest+ before creating links.
|
|
814
|
+
#
|
|
815
|
+
# Raises an exception if +dest+ is the path to an existing file or directory
|
|
816
|
+
# and keyword argument <tt>remove_destination: true</tt> is not given.
|
|
817
|
+
#
|
|
818
|
+
# Related: FileUtils.ln (has different options).
|
|
408
819
|
#
|
|
409
820
|
def link_entry(src, dest, dereference_root = false, remove_destination = false)
|
|
410
821
|
Entry_.new(src, nil, dereference_root).traverse do |ent|
|
|
@@ -415,16 +826,59 @@ module FileUtils
|
|
|
415
826
|
end
|
|
416
827
|
module_function :link_entry
|
|
417
828
|
|
|
829
|
+
# Copies files.
|
|
830
|
+
#
|
|
831
|
+
# Arguments +src+ (a single path or an array of paths)
|
|
832
|
+
# and +dest+ (a single path)
|
|
833
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
418
834
|
#
|
|
419
|
-
#
|
|
420
|
-
# copies +src+ to +dest
|
|
835
|
+
# If +src+ is the path to a file and +dest+ is not the path to a directory,
|
|
836
|
+
# copies +src+ to +dest+:
|
|
421
837
|
#
|
|
422
|
-
#
|
|
838
|
+
# FileUtils.touch('src0.txt')
|
|
839
|
+
# File.exist?('dest0.txt') # => false
|
|
840
|
+
# FileUtils.cp('src0.txt', 'dest0.txt')
|
|
841
|
+
# File.file?('dest0.txt') # => true
|
|
423
842
|
#
|
|
424
|
-
#
|
|
425
|
-
#
|
|
426
|
-
#
|
|
427
|
-
# FileUtils.
|
|
843
|
+
# If +src+ is the path to a file and +dest+ is the path to a directory,
|
|
844
|
+
# copies +src+ to <tt>dest/src</tt>:
|
|
845
|
+
#
|
|
846
|
+
# FileUtils.touch('src1.txt')
|
|
847
|
+
# FileUtils.mkdir('dest1')
|
|
848
|
+
# FileUtils.cp('src1.txt', 'dest1')
|
|
849
|
+
# File.file?('dest1/src1.txt') # => true
|
|
850
|
+
#
|
|
851
|
+
# If +src+ is an array of paths to files and +dest+ is the path to a directory,
|
|
852
|
+
# copies from each +src+ to +dest+:
|
|
853
|
+
#
|
|
854
|
+
# src_file_paths = ['src2.txt', 'src2.dat']
|
|
855
|
+
# FileUtils.touch(src_file_paths)
|
|
856
|
+
# FileUtils.mkdir('dest2')
|
|
857
|
+
# FileUtils.cp(src_file_paths, 'dest2')
|
|
858
|
+
# File.file?('dest2/src2.txt') # => true
|
|
859
|
+
# File.file?('dest2/src2.dat') # => true
|
|
860
|
+
#
|
|
861
|
+
# Keyword arguments:
|
|
862
|
+
#
|
|
863
|
+
# - <tt>preserve: true</tt> - preserves file times.
|
|
864
|
+
# - <tt>noop: true</tt> - does not copy files.
|
|
865
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
866
|
+
#
|
|
867
|
+
# FileUtils.cp('src0.txt', 'dest0.txt', noop: true, verbose: true)
|
|
868
|
+
# FileUtils.cp('src1.txt', 'dest1', noop: true, verbose: true)
|
|
869
|
+
# FileUtils.cp(src_file_paths, 'dest2', noop: true, verbose: true)
|
|
870
|
+
#
|
|
871
|
+
# Output:
|
|
872
|
+
#
|
|
873
|
+
# cp src0.txt dest0.txt
|
|
874
|
+
# cp src1.txt dest1
|
|
875
|
+
# cp src2.txt src2.dat dest2
|
|
876
|
+
#
|
|
877
|
+
# Raises an exception if +src+ is a directory.
|
|
878
|
+
#
|
|
879
|
+
# FileUtils.copy is an alias for FileUtils.cp.
|
|
880
|
+
#
|
|
881
|
+
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
|
|
428
882
|
#
|
|
429
883
|
def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
|
|
430
884
|
fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
|
|
@@ -438,30 +892,105 @@ module FileUtils
|
|
|
438
892
|
alias copy cp
|
|
439
893
|
module_function :copy
|
|
440
894
|
|
|
441
|
-
#
|
|
442
|
-
#
|
|
443
|
-
#
|
|
444
|
-
# +
|
|
445
|
-
#
|
|
446
|
-
#
|
|
447
|
-
#
|
|
448
|
-
#
|
|
449
|
-
#
|
|
450
|
-
# If +
|
|
451
|
-
#
|
|
452
|
-
#
|
|
453
|
-
# FileUtils.
|
|
454
|
-
#
|
|
455
|
-
#
|
|
456
|
-
# #
|
|
457
|
-
#
|
|
458
|
-
#
|
|
459
|
-
#
|
|
460
|
-
#
|
|
461
|
-
#
|
|
462
|
-
#
|
|
463
|
-
# FileUtils.cp_r
|
|
464
|
-
#
|
|
895
|
+
# Recursively copies files.
|
|
896
|
+
#
|
|
897
|
+
# Arguments +src+ (a single path or an array of paths)
|
|
898
|
+
# and +dest+ (a single path)
|
|
899
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
900
|
+
#
|
|
901
|
+
# The mode, owner, and group are retained in the copy;
|
|
902
|
+
# to change those, use FileUtils.install instead.
|
|
903
|
+
#
|
|
904
|
+
# If +src+ is the path to a file and +dest+ is not the path to a directory,
|
|
905
|
+
# copies +src+ to +dest+:
|
|
906
|
+
#
|
|
907
|
+
# FileUtils.touch('src0.txt')
|
|
908
|
+
# File.exist?('dest0.txt') # => false
|
|
909
|
+
# FileUtils.cp_r('src0.txt', 'dest0.txt')
|
|
910
|
+
# File.file?('dest0.txt') # => true
|
|
911
|
+
#
|
|
912
|
+
# If +src+ is the path to a file and +dest+ is the path to a directory,
|
|
913
|
+
# copies +src+ to <tt>dest/src</tt>:
|
|
914
|
+
#
|
|
915
|
+
# FileUtils.touch('src1.txt')
|
|
916
|
+
# FileUtils.mkdir('dest1')
|
|
917
|
+
# FileUtils.cp_r('src1.txt', 'dest1')
|
|
918
|
+
# File.file?('dest1/src1.txt') # => true
|
|
919
|
+
#
|
|
920
|
+
# If +src+ is the path to a directory and +dest+ does not exist,
|
|
921
|
+
# recursively copies +src+ to +dest+:
|
|
922
|
+
#
|
|
923
|
+
# tree('src2')
|
|
924
|
+
# # => src2
|
|
925
|
+
# # |-- dir0
|
|
926
|
+
# # | |-- src0.txt
|
|
927
|
+
# # | `-- src1.txt
|
|
928
|
+
# # `-- dir1
|
|
929
|
+
# # |-- src2.txt
|
|
930
|
+
# # `-- src3.txt
|
|
931
|
+
# FileUtils.exist?('dest2') # => false
|
|
932
|
+
# FileUtils.cp_r('src2', 'dest2')
|
|
933
|
+
# tree('dest2')
|
|
934
|
+
# # => dest2
|
|
935
|
+
# # |-- dir0
|
|
936
|
+
# # | |-- src0.txt
|
|
937
|
+
# # | `-- src1.txt
|
|
938
|
+
# # `-- dir1
|
|
939
|
+
# # |-- src2.txt
|
|
940
|
+
# # `-- src3.txt
|
|
941
|
+
#
|
|
942
|
+
# If +src+ and +dest+ are paths to directories,
|
|
943
|
+
# recursively copies +src+ to <tt>dest/src</tt>:
|
|
944
|
+
#
|
|
945
|
+
# tree('src3')
|
|
946
|
+
# # => src3
|
|
947
|
+
# # |-- dir0
|
|
948
|
+
# # | |-- src0.txt
|
|
949
|
+
# # | `-- src1.txt
|
|
950
|
+
# # `-- dir1
|
|
951
|
+
# # |-- src2.txt
|
|
952
|
+
# # `-- src3.txt
|
|
953
|
+
# FileUtils.mkdir('dest3')
|
|
954
|
+
# FileUtils.cp_r('src3', 'dest3')
|
|
955
|
+
# tree('dest3')
|
|
956
|
+
# # => dest3
|
|
957
|
+
# # `-- src3
|
|
958
|
+
# # |-- dir0
|
|
959
|
+
# # | |-- src0.txt
|
|
960
|
+
# # | `-- src1.txt
|
|
961
|
+
# # `-- dir1
|
|
962
|
+
# # |-- src2.txt
|
|
963
|
+
# # `-- src3.txt
|
|
964
|
+
#
|
|
965
|
+
# If +src+ is an array of paths and +dest+ is a directory,
|
|
966
|
+
# recursively copies from each path in +src+ to +dest+;
|
|
967
|
+
# the paths in +src+ may point to files and/or directories.
|
|
968
|
+
#
|
|
969
|
+
# Keyword arguments:
|
|
970
|
+
#
|
|
971
|
+
# - <tt>dereference_root: false</tt> - if +src+ is a symbolic link,
|
|
972
|
+
# does not dereference it.
|
|
973
|
+
# - <tt>noop: true</tt> - does not copy files.
|
|
974
|
+
# - <tt>preserve: true</tt> - preserves file times.
|
|
975
|
+
# - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
|
|
976
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
977
|
+
#
|
|
978
|
+
# FileUtils.cp_r('src0.txt', 'dest0.txt', noop: true, verbose: true)
|
|
979
|
+
# FileUtils.cp_r('src1.txt', 'dest1', noop: true, verbose: true)
|
|
980
|
+
# FileUtils.cp_r('src2', 'dest2', noop: true, verbose: true)
|
|
981
|
+
# FileUtils.cp_r('src3', 'dest3', noop: true, verbose: true)
|
|
982
|
+
#
|
|
983
|
+
# Output:
|
|
984
|
+
#
|
|
985
|
+
# cp -r src0.txt dest0.txt
|
|
986
|
+
# cp -r src1.txt dest1
|
|
987
|
+
# cp -r src2 dest2
|
|
988
|
+
# cp -r src3 dest3
|
|
989
|
+
#
|
|
990
|
+
# Raises an exception of +src+ is the path to a directory
|
|
991
|
+
# and +dest+ is the path to a file.
|
|
992
|
+
#
|
|
993
|
+
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
|
|
465
994
|
#
|
|
466
995
|
def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil,
|
|
467
996
|
dereference_root: true, remove_destination: nil)
|
|
@@ -473,21 +1002,50 @@ module FileUtils
|
|
|
473
1002
|
end
|
|
474
1003
|
module_function :cp_r
|
|
475
1004
|
|
|
1005
|
+
# Recursively copies files from +src+ to +dest+.
|
|
1006
|
+
#
|
|
1007
|
+
# Arguments +src+ and +dest+
|
|
1008
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
1009
|
+
#
|
|
1010
|
+
# If +src+ is the path to a file, copies +src+ to +dest+:
|
|
1011
|
+
#
|
|
1012
|
+
# FileUtils.touch('src0.txt')
|
|
1013
|
+
# File.exist?('dest0.txt') # => false
|
|
1014
|
+
# FileUtils.copy_entry('src0.txt', 'dest0.txt')
|
|
1015
|
+
# File.file?('dest0.txt') # => true
|
|
476
1016
|
#
|
|
477
|
-
#
|
|
478
|
-
# If +src+ is a directory, this method copies its contents recursively.
|
|
479
|
-
# This method preserves file types, c.f. symlink, directory...
|
|
480
|
-
# (FIFO, device files and etc. are not supported yet)
|
|
1017
|
+
# If +src+ is a directory, recursively copies +src+ to +dest+:
|
|
481
1018
|
#
|
|
482
|
-
#
|
|
483
|
-
#
|
|
1019
|
+
# tree('src1')
|
|
1020
|
+
# # => src1
|
|
1021
|
+
# # |-- dir0
|
|
1022
|
+
# # | |-- src0.txt
|
|
1023
|
+
# # | `-- src1.txt
|
|
1024
|
+
# # `-- dir1
|
|
1025
|
+
# # |-- src2.txt
|
|
1026
|
+
# # `-- src3.txt
|
|
1027
|
+
# FileUtils.copy_entry('src1', 'dest1')
|
|
1028
|
+
# tree('dest1')
|
|
1029
|
+
# # => dest1
|
|
1030
|
+
# # |-- dir0
|
|
1031
|
+
# # | |-- src0.txt
|
|
1032
|
+
# # | `-- src1.txt
|
|
1033
|
+
# # `-- dir1
|
|
1034
|
+
# # |-- src2.txt
|
|
1035
|
+
# # `-- src3.txt
|
|
484
1036
|
#
|
|
485
|
-
#
|
|
486
|
-
#
|
|
1037
|
+
# The recursive copying preserves file types for regular files,
|
|
1038
|
+
# directories, and symbolic links;
|
|
1039
|
+
# other file types (FIFO streams, device files, etc.) are not supported.
|
|
487
1040
|
#
|
|
488
|
-
#
|
|
1041
|
+
# Keyword arguments:
|
|
489
1042
|
#
|
|
490
|
-
#
|
|
1043
|
+
# - <tt>dereference_root: true</tt> - if +src+ is a symbolic link,
|
|
1044
|
+
# follows the link.
|
|
1045
|
+
# - <tt>preserve: true</tt> - preserves file times.
|
|
1046
|
+
# - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
|
|
1047
|
+
#
|
|
1048
|
+
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
|
|
491
1049
|
#
|
|
492
1050
|
def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
|
|
493
1051
|
if dereference_root
|
|
@@ -505,9 +1063,25 @@ module FileUtils
|
|
|
505
1063
|
end
|
|
506
1064
|
module_function :copy_entry
|
|
507
1065
|
|
|
1066
|
+
# Copies file from +src+ to +dest+, which should not be directories.
|
|
1067
|
+
#
|
|
1068
|
+
# Arguments +src+ and +dest+
|
|
1069
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
1070
|
+
#
|
|
1071
|
+
# Examples:
|
|
1072
|
+
#
|
|
1073
|
+
# FileUtils.touch('src0.txt')
|
|
1074
|
+
# FileUtils.copy_file('src0.txt', 'dest0.txt')
|
|
1075
|
+
# File.file?('dest0.txt') # => true
|
|
508
1076
|
#
|
|
509
|
-
#
|
|
510
|
-
#
|
|
1077
|
+
# Keyword arguments:
|
|
1078
|
+
#
|
|
1079
|
+
# - <tt>dereference: false</tt> - if +src+ is a symbolic link,
|
|
1080
|
+
# does not follow the link.
|
|
1081
|
+
# - <tt>preserve: true</tt> - preserves file times.
|
|
1082
|
+
# - <tt>remove_destination: true</tt> - removes +dest+ before copying files.
|
|
1083
|
+
#
|
|
1084
|
+
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
|
|
511
1085
|
#
|
|
512
1086
|
def copy_file(src, dest, preserve = false, dereference = true)
|
|
513
1087
|
ent = Entry_.new(src, nil, dereference)
|
|
@@ -516,25 +1090,81 @@ module FileUtils
|
|
|
516
1090
|
end
|
|
517
1091
|
module_function :copy_file
|
|
518
1092
|
|
|
1093
|
+
# Copies \IO stream +src+ to \IO stream +dest+ via
|
|
1094
|
+
# {IO.copy_stream}[https://docs.ruby-lang.org/en/master/IO.html#method-c-copy_stream].
|
|
519
1095
|
#
|
|
520
|
-
#
|
|
521
|
-
# +src+ must respond to #read(n) and
|
|
522
|
-
# +dest+ must respond to #write(str).
|
|
1096
|
+
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
|
|
523
1097
|
#
|
|
524
1098
|
def copy_stream(src, dest)
|
|
525
1099
|
IO.copy_stream(src, dest)
|
|
526
1100
|
end
|
|
527
1101
|
module_function :copy_stream
|
|
528
1102
|
|
|
529
|
-
#
|
|
530
|
-
#
|
|
531
|
-
#
|
|
532
|
-
#
|
|
533
|
-
#
|
|
534
|
-
#
|
|
535
|
-
#
|
|
536
|
-
#
|
|
537
|
-
#
|
|
1103
|
+
# Moves entries.
|
|
1104
|
+
#
|
|
1105
|
+
# Arguments +src+ (a single path or an array of paths)
|
|
1106
|
+
# and +dest+ (a single path)
|
|
1107
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
1108
|
+
#
|
|
1109
|
+
# If +src+ and +dest+ are on different file systems,
|
|
1110
|
+
# first copies, then removes +src+.
|
|
1111
|
+
#
|
|
1112
|
+
# May cause a local vulnerability if not called with keyword argument
|
|
1113
|
+
# <tt>secure: true</tt>;
|
|
1114
|
+
# see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
|
|
1115
|
+
#
|
|
1116
|
+
# If +src+ is the path to a single file or directory and +dest+ does not exist,
|
|
1117
|
+
# moves +src+ to +dest+:
|
|
1118
|
+
#
|
|
1119
|
+
# tree('src0')
|
|
1120
|
+
# # => src0
|
|
1121
|
+
# # |-- src0.txt
|
|
1122
|
+
# # `-- src1.txt
|
|
1123
|
+
# File.exist?('dest0') # => false
|
|
1124
|
+
# FileUtils.mv('src0', 'dest0')
|
|
1125
|
+
# File.exist?('src0') # => false
|
|
1126
|
+
# tree('dest0')
|
|
1127
|
+
# # => dest0
|
|
1128
|
+
# # |-- src0.txt
|
|
1129
|
+
# # `-- src1.txt
|
|
1130
|
+
#
|
|
1131
|
+
# If +src+ is an array of paths to files and directories
|
|
1132
|
+
# and +dest+ is the path to a directory,
|
|
1133
|
+
# copies from each path in the array to +dest+:
|
|
1134
|
+
#
|
|
1135
|
+
# File.file?('src1.txt') # => true
|
|
1136
|
+
# tree('src1')
|
|
1137
|
+
# # => src1
|
|
1138
|
+
# # |-- src.dat
|
|
1139
|
+
# # `-- src.txt
|
|
1140
|
+
# Dir.empty?('dest1') # => true
|
|
1141
|
+
# FileUtils.mv(['src1.txt', 'src1'], 'dest1')
|
|
1142
|
+
# tree('dest1')
|
|
1143
|
+
# # => dest1
|
|
1144
|
+
# # |-- src1
|
|
1145
|
+
# # | |-- src.dat
|
|
1146
|
+
# # | `-- src.txt
|
|
1147
|
+
# # `-- src1.txt
|
|
1148
|
+
#
|
|
1149
|
+
# Keyword arguments:
|
|
1150
|
+
#
|
|
1151
|
+
# - <tt>force: true</tt> - if the move includes removing +src+
|
|
1152
|
+
# (that is, if +src+ and +dest+ are on different file systems),
|
|
1153
|
+
# ignores raised exceptions of StandardError and its descendants.
|
|
1154
|
+
# - <tt>noop: true</tt> - does not move files.
|
|
1155
|
+
# - <tt>secure: true</tt> - removes +src+ securely;
|
|
1156
|
+
# see details at FileUtils.remove_entry_secure.
|
|
1157
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
1158
|
+
#
|
|
1159
|
+
# FileUtils.mv('src0', 'dest0', noop: true, verbose: true)
|
|
1160
|
+
# FileUtils.mv(['src1.txt', 'src1'], 'dest1', noop: true, verbose: true)
|
|
1161
|
+
#
|
|
1162
|
+
# Output:
|
|
1163
|
+
#
|
|
1164
|
+
# mv src0 dest0
|
|
1165
|
+
# mv src1.txt src1 dest1
|
|
1166
|
+
#
|
|
1167
|
+
# FileUtils.move is an alias for FileUtils.mv.
|
|
538
1168
|
#
|
|
539
1169
|
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
|
|
540
1170
|
fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
|
|
@@ -568,13 +1198,34 @@ module FileUtils
|
|
|
568
1198
|
alias move mv
|
|
569
1199
|
module_function :move
|
|
570
1200
|
|
|
1201
|
+
# Removes entries at the paths in the given +list+
|
|
1202
|
+
# (a single path or an array of paths)
|
|
1203
|
+
# returns +list+, if it is an array, <tt>[list]</tt> otherwise.
|
|
1204
|
+
#
|
|
1205
|
+
# Argument +list+ or its elements
|
|
1206
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
1207
|
+
#
|
|
1208
|
+
# With no keyword arguments, removes files at the paths given in +list+:
|
|
1209
|
+
#
|
|
1210
|
+
# FileUtils.touch(['src0.txt', 'src0.dat'])
|
|
1211
|
+
# FileUtils.rm(['src0.dat', 'src0.txt']) # => ["src0.dat", "src0.txt"]
|
|
571
1212
|
#
|
|
572
|
-
#
|
|
573
|
-
# All StandardErrors are ignored when the :force option is set.
|
|
1213
|
+
# Keyword arguments:
|
|
574
1214
|
#
|
|
575
|
-
#
|
|
576
|
-
#
|
|
577
|
-
#
|
|
1215
|
+
# - <tt>force: true</tt> - ignores raised exceptions of StandardError
|
|
1216
|
+
# and its descendants.
|
|
1217
|
+
# - <tt>noop: true</tt> - does not remove files; returns +nil+.
|
|
1218
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
1219
|
+
#
|
|
1220
|
+
# FileUtils.rm(['src0.dat', 'src0.txt'], noop: true, verbose: true)
|
|
1221
|
+
#
|
|
1222
|
+
# Output:
|
|
1223
|
+
#
|
|
1224
|
+
# rm src0.dat src0.txt
|
|
1225
|
+
#
|
|
1226
|
+
# FileUtils.remove is an alias for FileUtils.rm.
|
|
1227
|
+
#
|
|
1228
|
+
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
|
|
578
1229
|
#
|
|
579
1230
|
def rm(list, force: nil, noop: nil, verbose: nil)
|
|
580
1231
|
list = fu_list(list)
|
|
@@ -590,10 +1241,18 @@ module FileUtils
|
|
|
590
1241
|
alias remove rm
|
|
591
1242
|
module_function :remove
|
|
592
1243
|
|
|
1244
|
+
# Equivalent to:
|
|
593
1245
|
#
|
|
594
|
-
#
|
|
1246
|
+
# FileUtils.rm(list, force: true, **kwargs)
|
|
595
1247
|
#
|
|
596
|
-
#
|
|
1248
|
+
# Argument +list+ (a single path or an array of paths)
|
|
1249
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
1250
|
+
#
|
|
1251
|
+
# See FileUtils.rm for keyword arguments.
|
|
1252
|
+
#
|
|
1253
|
+
# FileUtils.safe_unlink is an alias for FileUtils.rm_f.
|
|
1254
|
+
#
|
|
1255
|
+
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
|
|
597
1256
|
#
|
|
598
1257
|
def rm_f(list, noop: nil, verbose: nil)
|
|
599
1258
|
rm list, force: true, noop: noop, verbose: verbose
|
|
@@ -603,24 +1262,55 @@ module FileUtils
|
|
|
603
1262
|
alias safe_unlink rm_f
|
|
604
1263
|
module_function :safe_unlink
|
|
605
1264
|
|
|
1265
|
+
# Removes entries at the paths in the given +list+
|
|
1266
|
+
# (a single path or an array of paths);
|
|
1267
|
+
# returns +list+, if it is an array, <tt>[list]</tt> otherwise.
|
|
1268
|
+
#
|
|
1269
|
+
# Argument +list+ or its elements
|
|
1270
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
606
1271
|
#
|
|
607
|
-
#
|
|
608
|
-
#
|
|
609
|
-
#
|
|
1272
|
+
# May cause a local vulnerability if not called with keyword argument
|
|
1273
|
+
# <tt>secure: true</tt>;
|
|
1274
|
+
# see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
|
|
610
1275
|
#
|
|
611
|
-
#
|
|
612
|
-
# FileUtils.rm_r 'some_dir', force: true
|
|
1276
|
+
# For each file path, removes the file at that path:
|
|
613
1277
|
#
|
|
614
|
-
#
|
|
615
|
-
#
|
|
616
|
-
#
|
|
617
|
-
#
|
|
618
|
-
# system has symbolic link. For secure removing, read the documentation
|
|
619
|
-
# of remove_entry_secure carefully, and set :secure option to true.
|
|
620
|
-
# Default is <tt>secure: false</tt>.
|
|
1278
|
+
# FileUtils.touch(['src0.txt', 'src0.dat'])
|
|
1279
|
+
# FileUtils.rm_r(['src0.dat', 'src0.txt'])
|
|
1280
|
+
# File.exist?('src0.txt') # => false
|
|
1281
|
+
# File.exist?('src0.dat') # => false
|
|
621
1282
|
#
|
|
622
|
-
#
|
|
623
|
-
#
|
|
1283
|
+
# For each directory path, recursively removes files and directories:
|
|
1284
|
+
#
|
|
1285
|
+
# tree('src1')
|
|
1286
|
+
# # => src1
|
|
1287
|
+
# # |-- dir0
|
|
1288
|
+
# # | |-- src0.txt
|
|
1289
|
+
# # | `-- src1.txt
|
|
1290
|
+
# # `-- dir1
|
|
1291
|
+
# # |-- src2.txt
|
|
1292
|
+
# # `-- src3.txt
|
|
1293
|
+
# FileUtils.rm_r('src1')
|
|
1294
|
+
# File.exist?('src1') # => false
|
|
1295
|
+
#
|
|
1296
|
+
# Keyword arguments:
|
|
1297
|
+
#
|
|
1298
|
+
# - <tt>force: true</tt> - ignores raised exceptions of StandardError
|
|
1299
|
+
# and its descendants.
|
|
1300
|
+
# - <tt>noop: true</tt> - does not remove entries; returns +nil+.
|
|
1301
|
+
# - <tt>secure: true</tt> - removes +src+ securely;
|
|
1302
|
+
# see details at FileUtils.remove_entry_secure.
|
|
1303
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
1304
|
+
#
|
|
1305
|
+
# FileUtils.rm_r(['src0.dat', 'src0.txt'], noop: true, verbose: true)
|
|
1306
|
+
# FileUtils.rm_r('src1', noop: true, verbose: true)
|
|
1307
|
+
#
|
|
1308
|
+
# Output:
|
|
1309
|
+
#
|
|
1310
|
+
# rm -r src0.dat src0.txt
|
|
1311
|
+
# rm -r src1
|
|
1312
|
+
#
|
|
1313
|
+
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
|
|
624
1314
|
#
|
|
625
1315
|
def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
|
|
626
1316
|
list = fu_list(list)
|
|
@@ -636,13 +1326,22 @@ module FileUtils
|
|
|
636
1326
|
end
|
|
637
1327
|
module_function :rm_r
|
|
638
1328
|
|
|
1329
|
+
# Equivalent to:
|
|
1330
|
+
#
|
|
1331
|
+
# FileUtils.rm_r(list, force: true, **kwargs)
|
|
639
1332
|
#
|
|
640
|
-
#
|
|
1333
|
+
# Argument +list+ or its elements
|
|
1334
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
641
1335
|
#
|
|
642
|
-
#
|
|
1336
|
+
# May cause a local vulnerability if not called with keyword argument
|
|
1337
|
+
# <tt>secure: true</tt>;
|
|
1338
|
+
# see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
|
|
643
1339
|
#
|
|
644
|
-
#
|
|
645
|
-
#
|
|
1340
|
+
# See FileUtils.rm_r for keyword arguments.
|
|
1341
|
+
#
|
|
1342
|
+
# FileUtils.rmtree is an alias for FileUtils.rm_rf.
|
|
1343
|
+
#
|
|
1344
|
+
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
|
|
646
1345
|
#
|
|
647
1346
|
def rm_rf(list, noop: nil, verbose: nil, secure: nil)
|
|
648
1347
|
rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
|
|
@@ -652,37 +1351,20 @@ module FileUtils
|
|
|
652
1351
|
alias rmtree rm_rf
|
|
653
1352
|
module_function :rmtree
|
|
654
1353
|
|
|
1354
|
+
# Securely removes the entry given by +path+,
|
|
1355
|
+
# which should be the entry for a regular file, a symbolic link,
|
|
1356
|
+
# or a directory.
|
|
655
1357
|
#
|
|
656
|
-
#
|
|
657
|
-
#
|
|
658
|
-
# remove it recursively. This method is required to avoid TOCTTOU
|
|
659
|
-
# (time-of-check-to-time-of-use) local security vulnerability of rm_r.
|
|
660
|
-
# #rm_r causes security hole when:
|
|
661
|
-
#
|
|
662
|
-
# * Parent directory is world writable (including /tmp).
|
|
663
|
-
# * Removing directory tree includes world writable directory.
|
|
664
|
-
# * The system has symbolic link.
|
|
665
|
-
#
|
|
666
|
-
# To avoid this security hole, this method applies special preprocess.
|
|
667
|
-
# If +path+ is a directory, this method chown(2) and chmod(2) all
|
|
668
|
-
# removing directories. This requires the current process is the
|
|
669
|
-
# owner of the removing whole directory tree, or is the super user (root).
|
|
670
|
-
#
|
|
671
|
-
# WARNING: You must ensure that *ALL* parent directories cannot be
|
|
672
|
-
# moved by other untrusted users. For example, parent directories
|
|
673
|
-
# should not be owned by untrusted users, and should not be world
|
|
674
|
-
# writable except when the sticky bit set.
|
|
1358
|
+
# Argument +path+
|
|
1359
|
+
# should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
|
|
675
1360
|
#
|
|
676
|
-
#
|
|
677
|
-
#
|
|
678
|
-
# work.
|
|
1361
|
+
# Avoids a local vulnerability that can exist in certain circumstances;
|
|
1362
|
+
# see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
|
|
679
1363
|
#
|
|
680
|
-
#
|
|
1364
|
+
# Optional argument +force+ specifies whether to ignore
|
|
1365
|
+
# raised exceptions of StandardError and its descendants.
|
|
681
1366
|
#
|
|
682
|
-
#
|
|
683
|
-
# * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
|
|
684
|
-
#
|
|
685
|
-
# For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
|
|
1367
|
+
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
|
|
686
1368
|
#
|
|
687
1369
|
def remove_entry_secure(path, force = false)
|
|
688
1370
|
unless fu_have_symlink?
|
|
@@ -770,12 +1452,17 @@ module FileUtils
|
|
|
770
1452
|
end
|
|
771
1453
|
private_module_function :fu_stat_identical_entry?
|
|
772
1454
|
|
|
1455
|
+
# Removes the entry given by +path+,
|
|
1456
|
+
# which should be the entry for a regular file, a symbolic link,
|
|
1457
|
+
# or a directory.
|
|
1458
|
+
#
|
|
1459
|
+
# Argument +path+
|
|
1460
|
+
# should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
|
|
773
1461
|
#
|
|
774
|
-
#
|
|
775
|
-
#
|
|
776
|
-
# If +path+ is a directory, remove it recursively.
|
|
1462
|
+
# Optional argument +force+ specifies whether to ignore
|
|
1463
|
+
# raised exceptions of StandardError and its descendants.
|
|
777
1464
|
#
|
|
778
|
-
#
|
|
1465
|
+
# Related: FileUtils.remove_entry_secure.
|
|
779
1466
|
#
|
|
780
1467
|
def remove_entry(path, force = false)
|
|
781
1468
|
Entry_.new(path).postorder_traverse do |ent|
|
|
@@ -790,9 +1477,16 @@ module FileUtils
|
|
|
790
1477
|
end
|
|
791
1478
|
module_function :remove_entry
|
|
792
1479
|
|
|
1480
|
+
# Removes the file entry given by +path+,
|
|
1481
|
+
# which should be the entry for a regular file or a symbolic link.
|
|
1482
|
+
#
|
|
1483
|
+
# Argument +path+
|
|
1484
|
+
# should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
|
|
1485
|
+
#
|
|
1486
|
+
# Optional argument +force+ specifies whether to ignore
|
|
1487
|
+
# raised exceptions of StandardError and its descendants.
|
|
793
1488
|
#
|
|
794
|
-
#
|
|
795
|
-
# This method ignores StandardError if +force+ is true.
|
|
1489
|
+
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
|
|
796
1490
|
#
|
|
797
1491
|
def remove_file(path, force = false)
|
|
798
1492
|
Entry_.new(path).remove_file
|
|
@@ -801,20 +1495,32 @@ module FileUtils
|
|
|
801
1495
|
end
|
|
802
1496
|
module_function :remove_file
|
|
803
1497
|
|
|
1498
|
+
# Recursively removes the directory entry given by +path+,
|
|
1499
|
+
# which should be the entry for a regular file, a symbolic link,
|
|
1500
|
+
# or a directory.
|
|
804
1501
|
#
|
|
805
|
-
#
|
|
806
|
-
#
|
|
1502
|
+
# Argument +path+
|
|
1503
|
+
# should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
|
|
1504
|
+
#
|
|
1505
|
+
# Optional argument +force+ specifies whether to ignore
|
|
1506
|
+
# raised exceptions of StandardError and its descendants.
|
|
1507
|
+
#
|
|
1508
|
+
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
|
|
807
1509
|
#
|
|
808
1510
|
def remove_dir(path, force = false)
|
|
809
1511
|
remove_entry path, force # FIXME?? check if it is a directory
|
|
810
1512
|
end
|
|
811
1513
|
module_function :remove_dir
|
|
812
1514
|
|
|
1515
|
+
# Returns +true+ if the contents of files +a+ and +b+ are identical,
|
|
1516
|
+
# +false+ otherwise.
|
|
813
1517
|
#
|
|
814
|
-
#
|
|
1518
|
+
# Arguments +a+ and +b+
|
|
1519
|
+
# should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
|
|
815
1520
|
#
|
|
816
|
-
#
|
|
817
|
-
#
|
|
1521
|
+
# FileUtils.identical? and FileUtils.cmp are aliases for FileUtils.compare_file.
|
|
1522
|
+
#
|
|
1523
|
+
# Related: FileUtils.compare_stream.
|
|
818
1524
|
#
|
|
819
1525
|
def compare_file(a, b)
|
|
820
1526
|
return false unless File.size(a) == File.size(b)
|
|
@@ -831,19 +1537,19 @@ module FileUtils
|
|
|
831
1537
|
module_function :identical?
|
|
832
1538
|
module_function :cmp
|
|
833
1539
|
|
|
1540
|
+
# Returns +true+ if the contents of streams +a+ and +b+ are identical,
|
|
1541
|
+
# +false+ otherwise.
|
|
1542
|
+
#
|
|
1543
|
+
# Arguments +a+ and +b+
|
|
1544
|
+
# should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
|
|
834
1545
|
#
|
|
835
|
-
#
|
|
1546
|
+
# Related: FileUtils.compare_file.
|
|
836
1547
|
#
|
|
837
1548
|
def compare_stream(a, b)
|
|
838
1549
|
bsize = fu_stream_blksize(a, b)
|
|
839
1550
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
sb = String.new(capacity: bsize)
|
|
843
|
-
else
|
|
844
|
-
sa = String.new
|
|
845
|
-
sb = String.new
|
|
846
|
-
end
|
|
1551
|
+
sa = String.new(capacity: bsize)
|
|
1552
|
+
sb = String.new(capacity: bsize)
|
|
847
1553
|
|
|
848
1554
|
begin
|
|
849
1555
|
a.read(bsize, sa)
|
|
@@ -854,13 +1560,69 @@ module FileUtils
|
|
|
854
1560
|
end
|
|
855
1561
|
module_function :compare_stream
|
|
856
1562
|
|
|
1563
|
+
# Copies a file entry.
|
|
1564
|
+
# See {install(1)}[https://man7.org/linux/man-pages/man1/install.1.html].
|
|
1565
|
+
#
|
|
1566
|
+
# Arguments +src+ (a single path or an array of paths)
|
|
1567
|
+
# and +dest+ (a single path)
|
|
1568
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments];
|
|
1569
|
+
#
|
|
1570
|
+
# If the entry at +dest+ does not exist, copies from +src+ to +dest+:
|
|
857
1571
|
#
|
|
858
|
-
#
|
|
859
|
-
#
|
|
860
|
-
#
|
|
1572
|
+
# File.read('src0.txt') # => "aaa\n"
|
|
1573
|
+
# File.exist?('dest0.txt') # => false
|
|
1574
|
+
# FileUtils.install('src0.txt', 'dest0.txt')
|
|
1575
|
+
# File.read('dest0.txt') # => "aaa\n"
|
|
861
1576
|
#
|
|
862
|
-
#
|
|
863
|
-
#
|
|
1577
|
+
# If +dest+ is a file entry, copies from +src+ to +dest+, overwriting:
|
|
1578
|
+
#
|
|
1579
|
+
# File.read('src1.txt') # => "aaa\n"
|
|
1580
|
+
# File.read('dest1.txt') # => "bbb\n"
|
|
1581
|
+
# FileUtils.install('src1.txt', 'dest1.txt')
|
|
1582
|
+
# File.read('dest1.txt') # => "aaa\n"
|
|
1583
|
+
#
|
|
1584
|
+
# If +dest+ is a directory entry, copies from +src+ to <tt>dest/src</tt>,
|
|
1585
|
+
# overwriting if necessary:
|
|
1586
|
+
#
|
|
1587
|
+
# File.read('src2.txt') # => "aaa\n"
|
|
1588
|
+
# File.read('dest2/src2.txt') # => "bbb\n"
|
|
1589
|
+
# FileUtils.install('src2.txt', 'dest2')
|
|
1590
|
+
# File.read('dest2/src2.txt') # => "aaa\n"
|
|
1591
|
+
#
|
|
1592
|
+
# If +src+ is an array of paths and +dest+ points to a directory,
|
|
1593
|
+
# copies each path +path+ in +src+ to <tt>dest/path</tt>:
|
|
1594
|
+
#
|
|
1595
|
+
# File.file?('src3.txt') # => true
|
|
1596
|
+
# File.file?('src3.dat') # => true
|
|
1597
|
+
# FileUtils.mkdir('dest3')
|
|
1598
|
+
# FileUtils.install(['src3.txt', 'src3.dat'], 'dest3')
|
|
1599
|
+
# File.file?('dest3/src3.txt') # => true
|
|
1600
|
+
# File.file?('dest3/src3.dat') # => true
|
|
1601
|
+
#
|
|
1602
|
+
# Keyword arguments:
|
|
1603
|
+
#
|
|
1604
|
+
# - <tt>group: <i>group</i></tt> - changes the group if not +nil+,
|
|
1605
|
+
# using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
|
|
1606
|
+
# - <tt>mode: <i>permissions</i></tt> - changes the permissions.
|
|
1607
|
+
# using {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
|
|
1608
|
+
# - <tt>noop: true</tt> - does not copy entries; returns +nil+.
|
|
1609
|
+
# - <tt>owner: <i>owner</i></tt> - changes the owner if not +nil+,
|
|
1610
|
+
# using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
|
|
1611
|
+
# - <tt>preserve: true</tt> - preserve timestamps
|
|
1612
|
+
# using {File.utime}[https://docs.ruby-lang.org/en/master/File.html#method-c-utime].
|
|
1613
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
1614
|
+
#
|
|
1615
|
+
# FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true)
|
|
1616
|
+
# FileUtils.install('src1.txt', 'dest1.txt', noop: true, verbose: true)
|
|
1617
|
+
# FileUtils.install('src2.txt', 'dest2', noop: true, verbose: true)
|
|
1618
|
+
#
|
|
1619
|
+
# Output:
|
|
1620
|
+
#
|
|
1621
|
+
# install -c src0.txt dest0.txt
|
|
1622
|
+
# install -c src1.txt dest1.txt
|
|
1623
|
+
# install -c src2.txt dest2
|
|
1624
|
+
#
|
|
1625
|
+
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
|
|
864
1626
|
#
|
|
865
1627
|
def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
|
|
866
1628
|
noop: nil, verbose: nil)
|
|
@@ -978,37 +1740,78 @@ module FileUtils
|
|
|
978
1740
|
end
|
|
979
1741
|
private_module_function :mode_to_s
|
|
980
1742
|
|
|
1743
|
+
# Changes permissions on the entries at the paths given in +list+
|
|
1744
|
+
# (a single path or an array of paths)
|
|
1745
|
+
# to the permissions given by +mode+;
|
|
1746
|
+
# returns +list+ if it is an array, <tt>[list]</tt> otherwise:
|
|
1747
|
+
#
|
|
1748
|
+
# - Modifies each entry that is a regular file using
|
|
1749
|
+
# {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
|
|
1750
|
+
# - Modifies each entry that is a symbolic link using
|
|
1751
|
+
# {File.lchmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchmod].
|
|
1752
|
+
#
|
|
1753
|
+
# Argument +list+ or its elements
|
|
1754
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
1755
|
+
#
|
|
1756
|
+
# Argument +mode+ may be either an integer or a string:
|
|
1757
|
+
#
|
|
1758
|
+
# - \Integer +mode+: represents the permission bits to be set:
|
|
1759
|
+
#
|
|
1760
|
+
# FileUtils.chmod(0755, 'src0.txt')
|
|
1761
|
+
# FileUtils.chmod(0644, ['src0.txt', 'src0.dat'])
|
|
1762
|
+
#
|
|
1763
|
+
# - \String +mode+: represents the permissions to be set:
|
|
1764
|
+
#
|
|
1765
|
+
# The string is of the form <tt>[targets][[operator][perms[,perms]]</tt>, where:
|
|
1766
|
+
#
|
|
1767
|
+
# - +targets+ may be any combination of these letters:
|
|
1768
|
+
#
|
|
1769
|
+
# - <tt>'u'</tt>: permissions apply to the file's owner.
|
|
1770
|
+
# - <tt>'g'</tt>: permissions apply to users in the file's group.
|
|
1771
|
+
# - <tt>'o'</tt>: permissions apply to other users not in the file's group.
|
|
1772
|
+
# - <tt>'a'</tt> (the default): permissions apply to all users.
|
|
1773
|
+
#
|
|
1774
|
+
# - +operator+ may be one of these letters:
|
|
1775
|
+
#
|
|
1776
|
+
# - <tt>'+'</tt>: adds permissions.
|
|
1777
|
+
# - <tt>'-'</tt>: removes permissions.
|
|
1778
|
+
# - <tt>'='</tt>: sets (replaces) permissions.
|
|
1779
|
+
#
|
|
1780
|
+
# - +perms+ (may be repeated, with separating commas)
|
|
1781
|
+
# may be any combination of these letters:
|
|
1782
|
+
#
|
|
1783
|
+
# - <tt>'r'</tt>: Read.
|
|
1784
|
+
# - <tt>'w'</tt>: Write.
|
|
1785
|
+
# - <tt>'x'</tt>: Execute (search, for a directory).
|
|
1786
|
+
# - <tt>'X'</tt>: Search (for a directories only;
|
|
1787
|
+
# must be used with <tt>'+'</tt>)
|
|
1788
|
+
# - <tt>'s'</tt>: Uid or gid.
|
|
1789
|
+
# - <tt>'t'</tt>: Sticky bit.
|
|
1790
|
+
#
|
|
1791
|
+
# Examples:
|
|
1792
|
+
#
|
|
1793
|
+
# FileUtils.chmod('u=wrx,go=rx', 'src1.txt')
|
|
1794
|
+
# FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby')
|
|
1795
|
+
#
|
|
1796
|
+
# Keyword arguments:
|
|
1797
|
+
#
|
|
1798
|
+
# - <tt>noop: true</tt> - does not change permissions; returns +nil+.
|
|
1799
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
1800
|
+
#
|
|
1801
|
+
# FileUtils.chmod(0755, 'src0.txt', noop: true, verbose: true)
|
|
1802
|
+
# FileUtils.chmod(0644, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
|
|
1803
|
+
# FileUtils.chmod('u=wrx,go=rx', 'src1.txt', noop: true, verbose: true)
|
|
1804
|
+
# FileUtils.chmod('u=wrx,go=rx', '/usr/bin/ruby', noop: true, verbose: true)
|
|
1805
|
+
#
|
|
1806
|
+
# Output:
|
|
1807
|
+
#
|
|
1808
|
+
# chmod 755 src0.txt
|
|
1809
|
+
# chmod 644 src0.txt src0.dat
|
|
1810
|
+
# chmod u=wrx,go=rx src1.txt
|
|
1811
|
+
# chmod u=wrx,go=rx /usr/bin/ruby
|
|
1812
|
+
#
|
|
1813
|
+
# Related: FileUtils.chmod_R.
|
|
981
1814
|
#
|
|
982
|
-
# Changes permission bits on the named files (in +list+) to the bit pattern
|
|
983
|
-
# represented by +mode+.
|
|
984
|
-
#
|
|
985
|
-
# +mode+ is the symbolic and absolute mode can be used.
|
|
986
|
-
#
|
|
987
|
-
# Absolute mode is
|
|
988
|
-
# FileUtils.chmod 0755, 'somecommand'
|
|
989
|
-
# FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
|
|
990
|
-
# FileUtils.chmod 0755, '/usr/bin/ruby', verbose: true
|
|
991
|
-
#
|
|
992
|
-
# Symbolic mode is
|
|
993
|
-
# FileUtils.chmod "u=wrx,go=rx", 'somecommand'
|
|
994
|
-
# FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb)
|
|
995
|
-
# FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', verbose: true
|
|
996
|
-
#
|
|
997
|
-
# "a" :: is user, group, other mask.
|
|
998
|
-
# "u" :: is user's mask.
|
|
999
|
-
# "g" :: is group's mask.
|
|
1000
|
-
# "o" :: is other's mask.
|
|
1001
|
-
# "w" :: is write permission.
|
|
1002
|
-
# "r" :: is read permission.
|
|
1003
|
-
# "x" :: is execute permission.
|
|
1004
|
-
# "X" ::
|
|
1005
|
-
# is execute permission for directories only, must be used in conjunction with "+"
|
|
1006
|
-
# "s" :: is uid, gid.
|
|
1007
|
-
# "t" :: is sticky bit.
|
|
1008
|
-
# "+" :: is added to a class given the specified mode.
|
|
1009
|
-
# "-" :: Is removed from a given class given mode.
|
|
1010
|
-
# "=" :: Is the exact nature of the class will be given a specified mode.
|
|
1011
|
-
|
|
1012
1815
|
def chmod(mode, list, noop: nil, verbose: nil)
|
|
1013
1816
|
list = fu_list(list)
|
|
1014
1817
|
fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose
|
|
@@ -1019,12 +1822,7 @@ module FileUtils
|
|
|
1019
1822
|
end
|
|
1020
1823
|
module_function :chmod
|
|
1021
1824
|
|
|
1022
|
-
#
|
|
1023
|
-
# Changes permission bits on the named files (in +list+)
|
|
1024
|
-
# to the bit pattern represented by +mode+.
|
|
1025
|
-
#
|
|
1026
|
-
# FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
|
|
1027
|
-
# FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
|
|
1825
|
+
# Like FileUtils.chmod, but changes permissions recursively.
|
|
1028
1826
|
#
|
|
1029
1827
|
def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
|
|
1030
1828
|
list = fu_list(list)
|
|
@@ -1044,15 +1842,68 @@ module FileUtils
|
|
|
1044
1842
|
end
|
|
1045
1843
|
module_function :chmod_R
|
|
1046
1844
|
|
|
1845
|
+
# Changes the owner and group on the entries at the paths given in +list+
|
|
1846
|
+
# (a single path or an array of paths)
|
|
1847
|
+
# to the given +user+ and +group+;
|
|
1848
|
+
# returns +list+ if it is an array, <tt>[list]</tt> otherwise:
|
|
1849
|
+
#
|
|
1850
|
+
# - Modifies each entry that is a regular file using
|
|
1851
|
+
# {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
|
|
1852
|
+
# - Modifies each entry that is a symbolic link using
|
|
1853
|
+
# {File.lchown}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchown].
|
|
1854
|
+
#
|
|
1855
|
+
# Argument +list+ or its elements
|
|
1856
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
1857
|
+
#
|
|
1858
|
+
# User and group:
|
|
1859
|
+
#
|
|
1860
|
+
# - Argument +user+ may be a user name or a user id;
|
|
1861
|
+
# if +nil+ or +-1+, the user is not changed.
|
|
1862
|
+
# - Argument +group+ may be a group name or a group id;
|
|
1863
|
+
# if +nil+ or +-1+, the group is not changed.
|
|
1864
|
+
# - The user must be a member of the group.
|
|
1865
|
+
#
|
|
1866
|
+
# Examples:
|
|
1867
|
+
#
|
|
1868
|
+
# # One path.
|
|
1869
|
+
# # User and group as string names.
|
|
1870
|
+
# File.stat('src0.txt').uid # => 1004
|
|
1871
|
+
# File.stat('src0.txt').gid # => 1004
|
|
1872
|
+
# FileUtils.chown('user2', 'group1', 'src0.txt')
|
|
1873
|
+
# File.stat('src0.txt').uid # => 1006
|
|
1874
|
+
# File.stat('src0.txt').gid # => 1005
|
|
1875
|
+
#
|
|
1876
|
+
# # User and group as uid and gid.
|
|
1877
|
+
# FileUtils.chown(1004, 1004, 'src0.txt')
|
|
1878
|
+
# File.stat('src0.txt').uid # => 1004
|
|
1879
|
+
# File.stat('src0.txt').gid # => 1004
|
|
1880
|
+
#
|
|
1881
|
+
# # Array of paths.
|
|
1882
|
+
# FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'])
|
|
1047
1883
|
#
|
|
1048
|
-
#
|
|
1049
|
-
#
|
|
1050
|
-
# may be an ID (Integer/String) or a name (String).
|
|
1051
|
-
# If +user+ or +group+ is nil, this method does not change
|
|
1052
|
-
# the attribute.
|
|
1884
|
+
# # Directory (not recursive).
|
|
1885
|
+
# FileUtils.chown('user2', 'group1', '.')
|
|
1053
1886
|
#
|
|
1054
|
-
#
|
|
1055
|
-
#
|
|
1887
|
+
# Keyword arguments:
|
|
1888
|
+
#
|
|
1889
|
+
# - <tt>noop: true</tt> - does not change permissions; returns +nil+.
|
|
1890
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
1891
|
+
#
|
|
1892
|
+
# FileUtils.chown('user2', 'group1', 'src0.txt', noop: true, verbose: true)
|
|
1893
|
+
# FileUtils.chown(1004, 1004, 'src0.txt', noop: true, verbose: true)
|
|
1894
|
+
# FileUtils.chown(1006, 1005, ['src0.txt', 'src0.dat'], noop: true, verbose: true)
|
|
1895
|
+
# FileUtils.chown('user2', 'group1', path, noop: true, verbose: true)
|
|
1896
|
+
# FileUtils.chown('user2', 'group1', '.', noop: true, verbose: true)
|
|
1897
|
+
#
|
|
1898
|
+
# Output:
|
|
1899
|
+
#
|
|
1900
|
+
# chown user2:group1 src0.txt
|
|
1901
|
+
# chown 1004:1004 src0.txt
|
|
1902
|
+
# chown 1006:1005 src0.txt src0.dat
|
|
1903
|
+
# chown user2:group1 src0.txt
|
|
1904
|
+
# chown user2:group1 .
|
|
1905
|
+
#
|
|
1906
|
+
# Related: FileUtils.chown_R.
|
|
1056
1907
|
#
|
|
1057
1908
|
def chown(user, group, list, noop: nil, verbose: nil)
|
|
1058
1909
|
list = fu_list(list)
|
|
@@ -1068,15 +1919,7 @@ module FileUtils
|
|
|
1068
1919
|
end
|
|
1069
1920
|
module_function :chown
|
|
1070
1921
|
|
|
1071
|
-
#
|
|
1072
|
-
# Changes owner and group on the named files (in +list+)
|
|
1073
|
-
# to the user +user+ and the group +group+ recursively.
|
|
1074
|
-
# +user+ and +group+ may be an ID (Integer/String) or
|
|
1075
|
-
# a name (String). If +user+ or +group+ is nil, this
|
|
1076
|
-
# method does not change the attribute.
|
|
1077
|
-
#
|
|
1078
|
-
# FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
|
|
1079
|
-
# FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', verbose: true
|
|
1922
|
+
# Like FileUtils.chown, but changes owner and group recursively.
|
|
1080
1923
|
#
|
|
1081
1924
|
def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
|
|
1082
1925
|
list = fu_list(list)
|
|
@@ -1127,12 +1970,50 @@ module FileUtils
|
|
|
1127
1970
|
end
|
|
1128
1971
|
private_module_function :fu_get_gid
|
|
1129
1972
|
|
|
1973
|
+
# Updates modification times (mtime) and access times (atime)
|
|
1974
|
+
# of the entries given by the paths in +list+
|
|
1975
|
+
# (a single path or an array of paths);
|
|
1976
|
+
# returns +list+ if it is an array, <tt>[list]</tt> otherwise.
|
|
1977
|
+
#
|
|
1978
|
+
# By default, creates an empty file for any path to a non-existent entry;
|
|
1979
|
+
# use keyword argument +nocreate+ to raise an exception instead.
|
|
1130
1980
|
#
|
|
1131
|
-
#
|
|
1132
|
-
#
|
|
1981
|
+
# Argument +list+ or its elements
|
|
1982
|
+
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
|
|
1133
1983
|
#
|
|
1134
|
-
#
|
|
1135
|
-
#
|
|
1984
|
+
# Examples:
|
|
1985
|
+
#
|
|
1986
|
+
# # Single path.
|
|
1987
|
+
# f = File.new('src0.txt') # Existing file.
|
|
1988
|
+
# f.atime # => 2022-06-10 11:11:21.200277 -0700
|
|
1989
|
+
# f.mtime # => 2022-06-10 11:11:21.200277 -0700
|
|
1990
|
+
# FileUtils.touch('src0.txt')
|
|
1991
|
+
# f = File.new('src0.txt')
|
|
1992
|
+
# f.atime # => 2022-06-11 08:28:09.8185343 -0700
|
|
1993
|
+
# f.mtime # => 2022-06-11 08:28:09.8185343 -0700
|
|
1994
|
+
#
|
|
1995
|
+
# # Array of paths.
|
|
1996
|
+
# FileUtils.touch(['src0.txt', 'src0.dat'])
|
|
1997
|
+
#
|
|
1998
|
+
# Keyword arguments:
|
|
1999
|
+
#
|
|
2000
|
+
# - <tt>mtime: <i>time</i></tt> - sets the entry's mtime to the given time,
|
|
2001
|
+
# instead of the current time.
|
|
2002
|
+
# - <tt>nocreate: true</tt> - raises an exception if the entry does not exist.
|
|
2003
|
+
# - <tt>noop: true</tt> - does not touch entries; returns +nil+.
|
|
2004
|
+
# - <tt>verbose: true</tt> - prints an equivalent command:
|
|
2005
|
+
#
|
|
2006
|
+
# FileUtils.touch('src0.txt', noop: true, verbose: true)
|
|
2007
|
+
# FileUtils.touch(['src0.txt', 'src0.dat'], noop: true, verbose: true)
|
|
2008
|
+
# FileUtils.touch(path, noop: true, verbose: true)
|
|
2009
|
+
#
|
|
2010
|
+
# Output:
|
|
2011
|
+
#
|
|
2012
|
+
# touch src0.txt
|
|
2013
|
+
# touch src0.txt src0.dat
|
|
2014
|
+
# touch src0.txt
|
|
2015
|
+
#
|
|
2016
|
+
# Related: FileUtils.uptodate?.
|
|
1136
2017
|
#
|
|
1137
2018
|
def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil)
|
|
1138
2019
|
list = fu_list(list)
|
|
@@ -1292,12 +2173,7 @@ module FileUtils
|
|
|
1292
2173
|
opts = {}
|
|
1293
2174
|
opts[:encoding] = fu_windows? ? ::Encoding::UTF_8 : path.encoding
|
|
1294
2175
|
|
|
1295
|
-
files =
|
|
1296
|
-
Dir.children(path, **opts)
|
|
1297
|
-
else
|
|
1298
|
-
Dir.entries(path(), **opts)
|
|
1299
|
-
.reject {|n| n == '.' or n == '..' }
|
|
1300
|
-
end
|
|
2176
|
+
files = Dir.children(path, **opts)
|
|
1301
2177
|
|
|
1302
2178
|
untaint = RUBY_VERSION < '2.7'
|
|
1303
2179
|
files.map {|n| Entry_.new(prefix(), join(rel(), untaint ? n.untaint : n)) }
|
|
@@ -1499,13 +2375,21 @@ module FileUtils
|
|
|
1499
2375
|
|
|
1500
2376
|
def postorder_traverse
|
|
1501
2377
|
if directory?
|
|
1502
|
-
|
|
2378
|
+
begin
|
|
2379
|
+
children = entries()
|
|
2380
|
+
rescue Errno::EACCES
|
|
2381
|
+
# Failed to get the list of children.
|
|
2382
|
+
# Assuming there is no children, try to process the parent directory.
|
|
2383
|
+
yield self
|
|
2384
|
+
return
|
|
2385
|
+
end
|
|
2386
|
+
|
|
2387
|
+
children.each do |ent|
|
|
1503
2388
|
ent.postorder_traverse do |e|
|
|
1504
2389
|
yield e
|
|
1505
2390
|
end
|
|
1506
2391
|
end
|
|
1507
2392
|
end
|
|
1508
|
-
ensure
|
|
1509
2393
|
yield self
|
|
1510
2394
|
end
|
|
1511
2395
|
|
|
@@ -1599,15 +2483,15 @@ module FileUtils
|
|
|
1599
2483
|
end
|
|
1600
2484
|
private_module_function :fu_each_src_dest
|
|
1601
2485
|
|
|
1602
|
-
def fu_each_src_dest0(src, dest) #:nodoc:
|
|
2486
|
+
def fu_each_src_dest0(src, dest, target_directory = true) #:nodoc:
|
|
1603
2487
|
if tmp = Array.try_convert(src)
|
|
1604
2488
|
tmp.each do |s|
|
|
1605
2489
|
s = File.path(s)
|
|
1606
|
-
yield s, File.join(dest, File.basename(s))
|
|
2490
|
+
yield s, (target_directory ? File.join(dest, File.basename(s)) : dest)
|
|
1607
2491
|
end
|
|
1608
2492
|
else
|
|
1609
2493
|
src = File.path(src)
|
|
1610
|
-
if File.directory?(dest)
|
|
2494
|
+
if target_directory and File.directory?(dest)
|
|
1611
2495
|
yield src, File.join(dest, File.basename(src))
|
|
1612
2496
|
else
|
|
1613
2497
|
yield src, File.path(dest)
|
|
@@ -1631,6 +2515,56 @@ module FileUtils
|
|
|
1631
2515
|
end
|
|
1632
2516
|
private_module_function :fu_output_message
|
|
1633
2517
|
|
|
2518
|
+
def fu_split_path(path)
|
|
2519
|
+
path = File.path(path)
|
|
2520
|
+
list = []
|
|
2521
|
+
until (parent, base = File.split(path); parent == path or parent == ".")
|
|
2522
|
+
list << base
|
|
2523
|
+
path = parent
|
|
2524
|
+
end
|
|
2525
|
+
list << path
|
|
2526
|
+
list.reverse!
|
|
2527
|
+
end
|
|
2528
|
+
private_module_function :fu_split_path
|
|
2529
|
+
|
|
2530
|
+
def fu_relative_components_from(target, base) #:nodoc:
|
|
2531
|
+
i = 0
|
|
2532
|
+
while target[i]&.== base[i]
|
|
2533
|
+
i += 1
|
|
2534
|
+
end
|
|
2535
|
+
Array.new(base.size-i, '..').concat(target[i..-1])
|
|
2536
|
+
end
|
|
2537
|
+
private_module_function :fu_relative_components_from
|
|
2538
|
+
|
|
2539
|
+
def fu_clean_components(*comp)
|
|
2540
|
+
comp.shift while comp.first == "."
|
|
2541
|
+
return comp if comp.empty?
|
|
2542
|
+
clean = [comp.shift]
|
|
2543
|
+
path = File.join(*clean, "") # ending with File::SEPARATOR
|
|
2544
|
+
while c = comp.shift
|
|
2545
|
+
if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
|
|
2546
|
+
clean.pop
|
|
2547
|
+
path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
|
|
2548
|
+
else
|
|
2549
|
+
clean << c
|
|
2550
|
+
path << c << "/"
|
|
2551
|
+
end
|
|
2552
|
+
end
|
|
2553
|
+
clean
|
|
2554
|
+
end
|
|
2555
|
+
private_module_function :fu_clean_components
|
|
2556
|
+
|
|
2557
|
+
if fu_windows?
|
|
2558
|
+
def fu_starting_path?(path)
|
|
2559
|
+
path&.start_with?(%r(\w:|/))
|
|
2560
|
+
end
|
|
2561
|
+
else
|
|
2562
|
+
def fu_starting_path?(path)
|
|
2563
|
+
path&.start_with?("/")
|
|
2564
|
+
end
|
|
2565
|
+
end
|
|
2566
|
+
private_module_function :fu_starting_path?
|
|
2567
|
+
|
|
1634
2568
|
# This hash table holds command options.
|
|
1635
2569
|
OPT_TABLE = {} #:nodoc: internal use only
|
|
1636
2570
|
(private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|
|
|
@@ -1640,50 +2574,49 @@ module FileUtils
|
|
|
1640
2574
|
|
|
1641
2575
|
public
|
|
1642
2576
|
|
|
2577
|
+
# Returns an array of the string names of \FileUtils methods
|
|
2578
|
+
# that accept one or more keyword arguments:
|
|
1643
2579
|
#
|
|
1644
|
-
#
|
|
1645
|
-
# arguments.
|
|
1646
|
-
#
|
|
1647
|
-
# p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
|
|
2580
|
+
# FileUtils.commands.sort.take(3) # => ["cd", "chdir", "chmod"]
|
|
1648
2581
|
#
|
|
1649
2582
|
def self.commands
|
|
1650
2583
|
OPT_TABLE.keys
|
|
1651
2584
|
end
|
|
1652
2585
|
|
|
2586
|
+
# Returns an array of the string keyword names:
|
|
1653
2587
|
#
|
|
1654
|
-
#
|
|
1655
|
-
#
|
|
1656
|
-
# p FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
|
|
2588
|
+
# FileUtils.options.take(3) # => ["noop", "verbose", "force"]
|
|
1657
2589
|
#
|
|
1658
2590
|
def self.options
|
|
1659
2591
|
OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
|
|
1660
2592
|
end
|
|
1661
2593
|
|
|
2594
|
+
# Returns +true+ if method +mid+ accepts the given option +opt+, +false+ otherwise;
|
|
2595
|
+
# the arguments may be strings or symbols:
|
|
1662
2596
|
#
|
|
1663
|
-
#
|
|
1664
|
-
#
|
|
1665
|
-
# p FileUtils.have_option?(:cp, :noop) #=> true
|
|
1666
|
-
# p FileUtils.have_option?(:rm, :force) #=> true
|
|
1667
|
-
# p FileUtils.have_option?(:rm, :preserve) #=> false
|
|
2597
|
+
# FileUtils.have_option?(:chmod, :noop) # => true
|
|
2598
|
+
# FileUtils.have_option?('chmod', 'secure') # => false
|
|
1668
2599
|
#
|
|
1669
2600
|
def self.have_option?(mid, opt)
|
|
1670
2601
|
li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
|
|
1671
2602
|
li.include?(opt)
|
|
1672
2603
|
end
|
|
1673
2604
|
|
|
2605
|
+
# Returns an array of the string keyword name for method +mid+;
|
|
2606
|
+
# the argument may be a string or a symbol:
|
|
1674
2607
|
#
|
|
1675
|
-
#
|
|
1676
|
-
#
|
|
1677
|
-
# p FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
|
|
2608
|
+
# FileUtils.options_of(:rm) # => ["force", "noop", "verbose"]
|
|
2609
|
+
# FileUtils.options_of('mv') # => ["force", "noop", "verbose", "secure"]
|
|
1678
2610
|
#
|
|
1679
2611
|
def self.options_of(mid)
|
|
1680
2612
|
OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
|
|
1681
2613
|
end
|
|
1682
2614
|
|
|
2615
|
+
# Returns an array of the string method names of the methods
|
|
2616
|
+
# that accept the given keyword option +opt+;
|
|
2617
|
+
# the argument must be a symbol:
|
|
1683
2618
|
#
|
|
1684
|
-
#
|
|
1685
|
-
#
|
|
1686
|
-
# p FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
|
|
2619
|
+
# FileUtils.collect_method(:preserve) # => ["cp", "copy", "cp_r", "install"]
|
|
1687
2620
|
#
|
|
1688
2621
|
def self.collect_method(opt)
|
|
1689
2622
|
OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
|