модуль Bundler::FileUtils
fileutils.rb
Авторское право © 2000-2007 Minero Aoki
Эта программа является свободной программной. Вы можете распространять/изменять эту программу на тех же условиях, что и ruby.
модуль Bundler::FileUtils
Пространство имён для нескольких методов работы с файлами: копирование, перемещение, удаление и т. д.
Module Функции
require 'bundler/vendor/fileutils/lib/fileutils'
Bundler::FileUtils.cd(dir, **options)
Bundler::FileUtils.cd(dir, **options) {|dir| block }
Bundler::FileUtils.pwd()
Bundler::FileUtils.mkdir(dir, **options)
Bundler::FileUtils.mkdir(list, **options)
Bundler::FileUtils.mkdir_p(dir, **options)
Bundler::FileUtils.mkdir_p(list, **options)
Bundler::FileUtils.rmdir(dir, **options)
Bundler::FileUtils.rmdir(list, **options)
Bundler::FileUtils.ln(target, link, **options)
Bundler::FileUtils.ln(targets, dir, **options)
Bundler::FileUtils.ln_s(target, link, **options)
Bundler::FileUtils.ln_s(targets, dir, **options)
Bundler::FileUtils.ln_sf(target, link, **options)
Bundler::FileUtils.cp(src, dest, **options)
Bundler::FileUtils.cp(list, dir, **options)
Bundler::FileUtils.cp_r(src, dest, **options)
Bundler::FileUtils.cp_r(list, dir, **options)
Bundler::FileUtils.mv(src, dest, **options)
Bundler::FileUtils.mv(list, dir, **options)
Bundler::FileUtils.rm(list, **options)
Bundler::FileUtils.rm_r(list, **options)
Bundler::FileUtils.rm_rf(list, **options)
Bundler::FileUtils.install(src, dest, **options)
Bundler::FileUtils.chmod(mode, list, **options)
Bundler::FileUtils.chmod_R(mode, list, **options)
Bundler::FileUtils.chown(user, group, list, **options)
Bundler::FileUtils.chown_R(user, group, list, **options)
Bundler::FileUtils.touch(list, **options)
Возможные options:
-
:force -
принудительное выполнение (перезапись файлов, если они существуют, удаление каталогов, если они не пустые и т.д.);
-
:verbose -
вывод команды, подлежащей выполнению, в синтаксисе bash, перед её выполнением;
-
:preserve -
сохранение группы, пользователя и времени изменения объекта при копировании;
-
:noop -
без изменений (можно использовать в сочетании с
:verbose, что выведет команду для выполнения)
Каждый метод документирует параметры, которые он поддерживает. Смотрите также методы ::commands, ::options и ::options_of для получения информации о командах и их параметрах.
Все методы, которые имеют понятие файла или каталога «источник», могут принимать либо один файл, либо список файлов в качестве аргумента. Примеры см. в документации к методам.
Есть некоторые методы «низкого уровня», которые не принимают ключевые аргументы:
Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false) Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true) Bundler::FileUtils.copy_stream(srcstream, deststream) Bundler::FileUtils.remove_entry(path, force = false) Bundler::FileUtils.remove_entry_secure(path, force = false) Bundler::FileUtils.remove_file(path, force = false) Bundler::FileUtils.compare_file(path_a, path_b) Bundler::FileUtils.compare_stream(stream_a, stream_b) Bundler::FileUtils.uptodate?(file, cmp_list)
модуль Bundler::FileUtils::Verbose
Этот модуль содержит все методы модуля Bundler::FileUtils, но выводит сообщения перед действием. Это эквивалентно передаче флага :verbose методам в модуле Bundler::FileUtils.
модуль Bundler::FileUtils::NoWrite
Этот модуль содержит все методы модуля Bundler::FileUtils, но никогда не изменяет файлы/каталоги. Это эквивалентно передаче флага :noop методам в модуле Bundler::FileUtils.
модуль Bundler::FileUtils::DryRun
Этот модуль содержит все методы модуля Bundler::FileUtils, но никогда не изменяет файлы/каталоги. Это эквивалентно передаче флагов :noop и :verbose методам в модуле Bundler::FileUtils.
Константы
- VERSION
Методы публичного класса
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 137
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 Изменяет текущую директорию на директорию dir.
Если этот метод вызывается с блоком, он возвращается к предыдущей рабочей директории после завершения выполнения блока.
Bundler::FileUtils.cd('/') # change directory
Bundler::FileUtils.cd('/', verbose: true) # change directory and report it
Bundler::FileUtils.cd('/') do # change directory
# ... # do something
end # return to original directory
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1012
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 Изменяет биты разрешений на указанные файлы (в list) на битовую схему, представленную mode.
Можно использовать символическую и абсолютную запись.
Абсолютный режим
Bundler::FileUtils.chmod 0755, 'somecommand' Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb) Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', verbose: true
Символический режим
Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand' Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb) Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', verbose: true
- “a”
-
это маска для пользователя, группы и других.
- “u”
-
это маска пользователя.
- “g”
-
это маска группы.
- “o”
-
это маска других.
- “w”
-
это разрешение на запись.
- “r”
-
это разрешение на чтение.
- “x”
-
это разрешение на выполнение.
- “X”
-
это разрешение на выполнение только для каталогов, должно использоваться в сочетании с “+”
- “s”
-
это uid, gid.
- “t”
-
это бит «вклейки».
- “+”
-
добавляется к классу, заданному указанным режимом.
- “-”
-
удаляется из данного класса, заданного указанным режимом.
- “=”
-
указывает точный характер класса, ему будет задан указанный режим.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1029
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 Изменяет биты разрешений на указанные файлы (в list) на битовую схему, представленную mode.
Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1057
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 Изменяет владельца и группу указанных файлов (в list) на пользователя user и группу group. user и group могут быть ID (целое число/строка) или именем (String). Если user или group равно null, этот метод не изменяет атрибут.
Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), verbose: true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1081
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 Рекурсивно изменяет владельца и группу указанных файлов (в list) на пользователя user и группу group. user и group могут быть ID (целое число/строка) или именем (String). Если user или group равно null, этот метод не изменяет атрибут.
Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs' Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', verbose: true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1679
def self.collect_method(opt)
OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
end Возвращает Array имён методов, которые имеют параметр opt.
p Bundler::FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1640 def self.commands OPT_TABLE.keys end
Возвращает Array имён высокоуровневых методов, которые принимают произвольные ключевые аргументы.
p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 816
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 Возвращает true, если содержимое файла a и файла b идентичны.
Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 834
def compare_stream(a, b)
bsize = fu_stream_blksize(a, b)
if RUBY_VERSION > "2.4"
sa = String.new(capacity: bsize)
sb = String.new(capacity: bsize)
else
sa = String.new
sb = String.new
end
begin
a.read(bsize, sa)
b.read(bsize, sb)
return true if sa.empty? && sb.empty?
end while sa == sb
false
end Возвращает true, если содержимое потока a и b идентичны.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 489
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 Копирует элемент файловой системы src в dest. Если src является каталогом, этот метод рекурсивно копирует его содержимое. Метод сохраняет типы файлов, см. символическую ссылку, каталог… (FIFO, файлы устройств и т.д. пока не поддерживаются)
И src и dest должны быть именами путей. src должен существовать, dest не должен существовать.
Если preserve равно true, этот метод сохраняет владельца, группу и время изменения. Разрешения копируются независимо от preserve.
Если dereference_root равно true, этот метод делает ссылку на корень дерева.
Если remove_destination равно true, этот метод удаляет каждый целевой файл перед копированием.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 509 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
Копирует содержимое файла src в dest. И src и dest должны быть именами путей.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 521 def copy_stream(src, dest) IO.copy_stream(src, dest) end
Копирует поток src в dest. src должен отвечать на read(n), а dest должен отвечать на write(str).
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 426
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 Копирует содержимое файла src в dest. Если dest является каталогом, копирует src в dest/src.
Если src является списком файлов, то dest должен быть каталогом.
Bundler::FileUtils.cp 'eval.c', 'eval.c.org' Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6' Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', verbose: true Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 339
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 Создаёт жёсткую ссылку src на dest. Если src является каталогом, этот метод создаёт ссылки на всё его содержимое рекурсивно. Если dest является каталогом, создаёт ссылки на src в dest/src.
src может быть списком файлов.
Если dereference_root равно true, этот метод делает ссылку на корень дерева.
Если remove_destination равно true, этот метод удаляет каждый целевой файл перед копированием.
Bundler::FileUtils.rm_r site_ruby + '/mylib', force: true
Bundler::FileUtils.cp_lr 'lib/', site_ruby + '/mylib'
# Examples of linking several files to target directory.
Bundler::FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail'
Bundler::FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', noop: true, verbose: true
# If you want to link all contents of a directory instead of the
# directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
# use the following code.
Bundler::FileUtils.cp_lr 'src/.', 'dest' # cp_lr('src', 'dest') makes dest/src, but this doesn't.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 463
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 Копирует src в dest. Если src является каталогом, этот метод копирует все его содержимое рекурсивно. Если dest является каталогом, копирует src в dest/src.
src может быть списком файлов.
Если dereference_root равно true, этот метод дескрипционирует корень дерева.
Если remove_destination равно true, этот метод удаляет каждый файл назначения перед копированием.
# Installing Ruby library "mylib" under the site_ruby
Bundler::FileUtils.rm_r site_ruby + '/mylib', force: true
Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib'
# Examples of copying several files to target directory.
Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', noop: true, verbose: true
# If you want to copy all contents of a directory instead of the
# directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
# use following code.
Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
# but this doesn't.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1660
def self.have_option?(mid, opt)
li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
li.include?(opt)
end Возвращает true, если метод mid имеет параметр opt.
p Bundler::FileUtils.have_option?(:cp, :noop) #=> true p Bundler::FileUtils.have_option?(:rm, :force) #=> true p Bundler::FileUtils.have_option?(:rm, :preserve) #=> false
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 862
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
copy_file s, d
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 Если src не совпадает с dest, копирует его и изменяет режим доступа на mode. Если dest является каталогом, назначение — dest/src. Этот метод удаляет пункт назначения перед копированием.
Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', mode: 0755, verbose: true Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', verbose: true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 406
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 Создаёт жёсткую ссылку на элемент файловой системы src в dest. Если src является каталогом, этот метод рекурсивно создаёт ссылки на его содержимое.
И src, и dest должны быть именами путей. src должен существовать, а dest — нет.
Если dereference_root равно true, этот метод дескрипционирует корень дерева.
Если remove_destination равно true, этот метод удаляет каждый файл назначения перед копированием.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 303
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 В первом случае создаёт жёсткую ссылку link, которая указывает на target. Если link уже существует, генерируется исключение Errno::EEXIST. Но если задан параметр force, переписывает link.
Bundler::FileUtils.ln 'gcc', 'cc', verbose: true Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
Во втором случае создаёт ссылку dir/target, указывающую на target. В третьем случае создаёт несколько жёстких ссылок в каталоге dir, указывающих на каждый элемент в targets. Если dir не является каталогом, генерируется исключение Errno::ENOTDIR.
Bundler::FileUtils.cd '/sbin' Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 369
def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
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 В первом случае создаёт символическую ссылку link, которая указывает на target. Если link уже существует, генерируется исключение Errno::EEXIST. Но если задан параметр force, переписывает link.
Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby' Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
Во втором случае создаёт ссылку dir/target, указывающую на target. В третьем случае создаёт несколько символических ссылок в каталоге dir, указывающих на каждый элемент в targets. Если dir не является каталогом, генерируется исключение Errno::ENOTDIR.
Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 390 def ln_sf(src, dest, noop: nil, verbose: nil) ln_s src, dest, force: true, noop: noop, verbose: verbose end
То же, что и
Bundler::FileUtils.ln_s(*args, force: true)
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 180
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 Создаёт один или несколько каталогов.
Bundler::FileUtils.mkdir 'test' Bundler::FileUtils.mkdir %w(tmp data) Bundler::FileUtils.mkdir 'notexist', noop: true # Does not really create. Bundler::FileUtils.mkdir 'tmp', mode: 0700
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 206
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.map {|path| remove_trailing_slash(path)}.each do |path|
# optimize for the most common case
begin
fu_mkdir path, mode
next
rescue SystemCallError
next if File.directory?(path)
end
stack = []
until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
stack.push path
path = File.dirname(path)
end
stack.pop # root directory should exist
stack.reverse_each do |dir|
begin
fu_mkdir dir, mode
rescue SystemCallError
raise unless File.directory?(dir)
end
end
end
return *list
end Создаёт каталог и все его родительские каталоги. Например,
Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
приводит к созданию следующих каталогов, если они не существуют.
-
/usr
-
/usr/local
-
/usr/local/lib
-
/usr/local/lib/ruby
Вы можете передавать несколько каталогов одновременно в списке.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 536
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 Перемещает файл(ы) src в dest. Если file и dest находятся на разных разделах диска, файл копируется, а затем удаляется исходный файл.
Bundler::FileUtils.mv 'badname.rb', 'goodname.rb'
Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', force: true # no error
Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', noop: true, verbose: true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1649
def self.options
OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
end Возвращает Array имён параметров.
p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1670
def self.options_of(mid)
OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
end Возвращает Array имён параметров метода mid.
p Bundler::FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 115 def pwd Dir.pwd end
Возвращает имя текущего каталога.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 805 def remove_dir(path, force = false) remove_entry path, force # FIXME?? check if it is a directory end
Удаляет директорию dir и её содержимое рекурсивно. Этот метод игнорирует StandardError, если force равно true.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 777
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 Этот метод удаляет элемент файловой системы path. path может быть обычным файлом, каталогом или чем-то ещё. Если path является каталогом, удаляет его рекурсивно.
См. также remove_entry_secure.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 684
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, Bundler::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 Этот метод удаляет элемент файловой системы path. path должен быть обычным файлом, каталогом или чем-то другим. Если path является каталогом, удаляет его рекурсивно. Этот метод необходим для предотвращения локальной уязвимости TOCTTOU (проверка состояния — время использования) метода rm_r. rm_r создаёт уязвимость в следующих случаях:
-
Родительский каталог имеет разрешение на запись для всех (включая /tmp).
-
Удаляемая структура каталогов включает каталог с разрешением на запись для всех.
-
На системе есть символьные ссылки.
Для предотвращения этой уязвимости, этот метод применяет специальную предобработку. Если path является каталогом, этот метод устанавливает права владения и доступа (chown(2) и chmod(2)) для всех удаляемых каталогов. Это требует, чтобы текущий процесс был владельцем всей удаляемой структуры каталогов или имел права суперпользователя (root).
ВНИМАНИЕ: Вы должны убедиться, что ВСЕ родительские каталоги не могут быть изменены другими неуполномоченными пользователями. Например, родительские каталоги не должны принадлежать неуполномоченным пользователям и не должны быть доступны для записи всем, за исключением случаев, когда установлен бит «приклеить» (sticky bit).
ВНИМАНИЕ: Только владелец удаляемой структуры каталогов или суперпользователь Unix (root) должен вызывать этот метод. В противном случае этот метод не сработает.
Подробнее об этой уязвимости можно узнать из примера с Perl:
Для fileutils.rb эта уязвимость была сообщена в [ruby-dev:26100].
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 794 def remove_file(path, force = false) Entry_.new(path).remove_file rescue raise unless force end
Удаляет файл path. Этот метод игнорирует StandardError, если force равно true.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 576
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 Удаляет файлы, указанные в list. Этот метод не может удалять каталоги. Все StandardErrors игнорируются, когда установлен параметр :force.
Bundler::FileUtils.rm %w( junk.txt dust.txt )
Bundler::FileUtils.rm Dir.glob('*.so')
Bundler::FileUtils.rm 'NotExistFile', force: true # never raises exception
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 595 def rm_f(list, noop: nil, verbose: nil) rm list, force: true, noop: noop, verbose: verbose end
Эквивалентно
Bundler::FileUtils.rm(list, force: true)
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 622
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 Удаляет файлы list[0] list[1]… Если list[n] является каталогом, удаляет всё его содержимое рекурсивно. Этот метод игнорирует StandardError, когда установлен параметр :force.
Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
Bundler::FileUtils.rm_r 'some_dir', force: true
ВНИМАНИЕ: Этот метод может вызвать локальную уязвимость, если один из родительских каталогов или удаляемая структура каталогов имеют разрешение на запись для всех (включая /tmp, права 1777), а текущий процесс имеет высокие привилегии, такие как права суперпользователя (root), и на системе есть символьные ссылки. Для безопасного удаления внимательно изучите документацию метода remove_entry_secure и установите параметр :secure в значение true. По умолчанию secure: false.
ПРИМЕЧАНИЕ: Этот метод вызывает remove_entry_secure, если установлен параметр :secure. См. также remove_entry_secure.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 644 def rm_rf(list, noop: nil, verbose: nil, secure: nil) rm_r list, force: true, noop: noop, verbose: verbose, secure: secure end
Эквивалентно
Bundler::FileUtils.rm_r(list, force: true)
ВНИМАНИЕ: Этот метод может вызвать локальную уязвимость. Сначала прочитайте документацию метода rm_r.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 263
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 Удаляет один или несколько каталогов.
Bundler::FileUtils.rmdir 'somedir' Bundler::FileUtils.rmdir %w(somedir anydir otherdir) # Does not really remove directory; outputs message. Bundler::FileUtils.rmdir 'somedir', verbose: true, noop: true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1137
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 Обновляет время последнего изменения (mtime) и время последнего доступа (atime) файлов в list. Файлы создаются, если они не существуют.
Bundler::FileUtils.touch 'timestamp'
Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 155
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 Возвращает true, если new новее всех old_list. Несуществующие файлы считаются более старыми, чем любой файл.
Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
system 'make hello.o'
Методы частного экземпляра
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 137
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 Изменяет текущую директорию на директорию dir.
Если этот метод вызывается с блоком, он возвращается к предыдущей рабочей директории после завершения выполнения блока.
Bundler::FileUtils.cd('/') # change directory
Bundler::FileUtils.cd('/', verbose: true) # change directory and report it
Bundler::FileUtils.cd('/') do # change directory
# ... # do something
end # return to original directory
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1012
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 Изменяет биты разрешений для указанных файлов (в list) на битовую комбинацию, представленную значением mode.
Можно использовать символический и абсолютный режим.
Абсолютный режим
Bundler::FileUtils.chmod 0755, 'somecommand' Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb) Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', verbose: true
Символический режим
Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand' Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb) Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', verbose: true
- “a”
-
— маска для пользователя, группы и других.
- “u”
-
— маска для пользователя.
- “g”
-
— маска для группы.
- “o”
-
— маска для других.
- “w”
-
— разрешение на запись.
- “r”
-
— разрешение на чтение.
- “x”
-
— разрешение на выполнение.
- “X”
-
— разрешение на выполнение только для каталогов, должно использоваться в сочетании с “+”
- “s”
-
— uid, gid.
- “t”
-
— бит «sticky».
- “+”
-
— добавляется к классу с заданным режимом.
- “-”
-
— удаляется из заданного класса с заданным режимом.
- “=”
-
— точный класс с заданным режимом.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1029
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 Изменяет биты разрешений для указанных файлов (в list) на битовую комбинацию, представленную значением mode рекурсивно.
Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1057
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 Изменяет владельца и группу для указанных файлов (в list) на пользователя user и группу group. user и group могут быть ID (целое число/строка) или именем (String). Если user или group равно nil, этот метод не изменяет атрибут.
Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), verbose: true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1081
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 Изменяет владельца и группу для указанных файлов (в list) на пользователя user и группу group рекурсивно. user и group могут быть ID (целое число/строка) или именем (String). Если user или group равно nil, этот метод не изменяет атрибут.
Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs' Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', verbose: true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 816
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 Возвращает true, если содержимое файла a и файла b идентичны.
Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true
Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 834
def compare_stream(a, b)
bsize = fu_stream_blksize(a, b)
if RUBY_VERSION > "2.4"
sa = String.new(capacity: bsize)
sb = String.new(capacity: bsize)
else
sa = String.new
sb = String.new
end
begin
a.read(bsize, sa)
b.read(bsize, sb)
return true if sa.empty? && sb.empty?
end while sa == sb
false
end Возвращает true, если содержимое потока a и b идентичны.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 489
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 Копирует элемент файловой системы src в dest. Если src является каталогом, этот метод копирует его содержимое рекурсивно. Метод сохраняет типы файлов, например, символьные ссылки, каталоги… (FIFO, файлы устройств и т. д. пока не поддерживаются)
И src, и dest должны быть именами путей. src должен существовать, dest не должен существовать.
Если preserve равно true, этот метод сохраняет владельца, группу и время изменения. Разрешения копируются независимо preserve.
Если dereference_root равно true, этот метод ссылается на корень дерева.
Если remove_destination равно true, этот метод удаляет каждый целевой файл перед копированием.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 509 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
Копирует содержимое файла src в dest. И src, и dest должны быть именами путей.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 521 def copy_stream(src, dest) IO.copy_stream(src, dest) end
Копирует поток src в dest. src должен отвечать на read(n), а dest должен отвечать на write(str).
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 426
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 Копирует содержимое файла src в dest. Если dest является каталогом, копирует src в dest/src.
Если src является списком файлов, то dest должен быть каталогом.
Bundler::FileUtils.cp 'eval.c', 'eval.c.org' Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6' Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', verbose: true Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 339
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 Создает жёсткую ссылку на src в dest. Если src является каталогом, этот метод создаёт ссылки на все его содержимое рекурсивно. Если dest является каталогом, создаёт ссылки на src в dest/src.
src может быть списком файлов.
Если dereference_root равно true, этот метод ссылается на корень дерева.
Если remove_destination равно true, этот метод удаляет каждый целевой файл перед копированием.
Bundler::FileUtils.rm_r site_ruby + '/mylib', force: true
Bundler::FileUtils.cp_lr 'lib/', site_ruby + '/mylib'
# Examples of linking several files to target directory.
Bundler::FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail'
Bundler::FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', noop: true, verbose: true
# If you want to link all contents of a directory instead of the
# directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
# use the following code.
Bundler::FileUtils.cp_lr 'src/.', 'dest' # cp_lr('src', 'dest') makes dest/src, but this doesn't.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 463
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 Копирует src в dest. Если src является каталогом, этот метод копирует все его содержимое рекурсивно. Если dest является каталогом, копирует src в dest/src.
src может быть списком файлов.
Если dereference_root равно true, этот метод ссылается на корень дерева.
Если remove_destination равно true, этот метод удаляет каждый целевой файл перед копированием.
# Installing Ruby library "mylib" under the site_ruby
Bundler::FileUtils.rm_r site_ruby + '/mylib', force: true
Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib'
# Examples of copying several files to target directory.
Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', noop: true, verbose: true
# If you want to copy all contents of a directory instead of the
# directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
# use following code.
Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
# but this doesn't.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 862
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
copy_file s, d
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 Если src не совпадает с dest, копирует его и изменяет разрешения на mode. Если dest — это каталог, назначение — dest/src. Этот метод удаляет назначение перед копированием.
Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', mode: 0755, verbose: true Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', verbose: true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 406
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 Создаёт жёсткую ссылку на системную запись src на dest. Если src — это каталог, этот метод создаёт ссылки на его содержимое рекурсивно.
И src, и dest должны быть именами путей. src должен существовать, dest не должен существовать.
Если dereference_root истинно, этот метод обращается к корню дерева.
Если remove_destination истинно, этот метод удаляет каждый целевой файл перед копированием.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 303
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 В первом случае создаётся жёсткая ссылка link, которая указывает на target. Если link уже существует, возникает исключение Errno::EEXIST. Но если установлен параметр force, перезаписывает link.
Bundler::FileUtils.ln 'gcc', 'cc', verbose: true Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
Во втором случае создаётся ссылка dir/target, указывающая на target. В третьем случае создаётся несколько жёстких ссылок в каталоге dir, указывающих на каждый элемент в targets. Если dir не является каталогом, возникает исключение Errno::ENOTDIR.
Bundler::FileUtils.cd '/sbin' Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 369
def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
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 В первом случае создаётся символическая ссылка link, которая указывает на target. Если link уже существует, возникает исключение Errno::EEXIST. Но если установлен параметр force, перезаписывает link.
Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby' Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
Во втором случае создаётся ссылка dir/target, указывающая на target. В третьем случае создаётся несколько символических ссылок в каталоге dir, указывающих на каждый элемент в targets. Если dir не является каталогом, возникает исключение Errno::ENOTDIR.
Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 390 def ln_sf(src, dest, noop: nil, verbose: nil) ln_s src, dest, force: true, noop: noop, verbose: verbose end
То же, что
Bundler::FileUtils.ln_s(*args, force: true)
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 180
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 Создаёт один или несколько каталогов.
Bundler::FileUtils.mkdir 'test' Bundler::FileUtils.mkdir %w(tmp data) Bundler::FileUtils.mkdir 'notexist', noop: true # Does not really create. Bundler::FileUtils.mkdir 'tmp', mode: 0700
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 206
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.map {|path| remove_trailing_slash(path)}.each do |path|
# optimize for the most common case
begin
fu_mkdir path, mode
next
rescue SystemCallError
next if File.directory?(path)
end
stack = []
until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
stack.push path
path = File.dirname(path)
end
stack.pop # root directory should exist
stack.reverse_each do |dir|
begin
fu_mkdir dir, mode
rescue SystemCallError
raise unless File.directory?(dir)
end
end
end
return *list
end Создаёт каталог и все его родительские каталоги. Например,
Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby'
приводит к созданию следующих каталогов, если они не существуют.
-
/usr
-
/usr/local
-
/usr/local/lib
-
/usr/local/lib/ruby
Вы можете передать несколько каталогов одновременно в списке.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 536
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 Перемещает файл(ы) src в dest. Если file и dest находятся на разных разделах диска, файл копируется, а затем удаляется исходный файл.
Bundler::FileUtils.mv 'badname.rb', 'goodname.rb'
Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', force: true # no error
Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', noop: true, verbose: true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 115 def pwd Dir.pwd end
Возвращает имя текущего каталога.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 805 def remove_dir(path, force = false) remove_entry path, force # FIXME?? check if it is a directory end
Удаляет каталог dir и его содержимое рекурсивно. Этот метод игнорирует StandardError, если force истинно.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 777
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 Этот метод удаляет системную запись path. path может быть обычным файлом, каталогом или чем-то ещё. Если path — это каталог, удаляет его рекурсивно.
См. также remove_entry_secure.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 684
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, Bundler::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 Этот метод удаляет элемент файловой системы path. path должен быть обычным файлом, каталогом или чем-то другим. Если path является каталогом, удалите его рекурсивно. Этот метод необходим для предотвращения локальной уязвимости безопасности TOCTTOU (время проверки — время использования) метода rm_r. rm_r создает уязвимость безопасности, когда:
-
Родительский каталог имеет разрешения на запись для всех (включая /tmp).
-
Дерево каталогов для удаления включает каталоги с разрешениями на запись для всех.
-
Система содержит символические ссылки.
Для предотвращения этой уязвимости безопасности этот метод применяет специальную предобработку. Если path является каталогом, этот метод использует chown(2) и chmod(2) для всех удаляемых каталогов. Это требует, чтобы текущий процесс был владельцем всего удаляемого дерева каталогов или являлся суперпользователем (root).
ПРЕДУПРЕЖДЕНИЕ: Вы должны убедиться, что ВСЕ родительские каталоги не могут быть изменены другими неуверенными пользователями. Например, родительские каталоги не должны принадлежать неуверенным пользователям и не должны иметь разрешения на запись для всех, за исключением случаев, когда установлен бит «sticky».
ПРЕДУПРЕЖДЕНИЕ: Этот метод должен вызываться только владельцем удаляемого дерева каталогов или суперпользователем Unix (root). В противном случае метод не будет работать.
Подробную информацию об этой уязвимости безопасности см. в случае Perl:
Для fileutils.rb эта уязвимость была доложена в [ruby-dev:26100].
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 794 def remove_file(path, force = false) Entry_.new(path).remove_file rescue raise unless force end
Удаляет файл path. Этот метод игнорирует StandardError, если force равно true.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 576
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 Удаляет файлы, указанные в list. Этот метод не может удалять каталоги. Все StandardErrors игнорируются, когда установлен параметр :force.
Bundler::FileUtils.rm %w( junk.txt dust.txt )
Bundler::FileUtils.rm Dir.glob('*.so')
Bundler::FileUtils.rm 'NotExistFile', force: true # never raises exception
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 595 def rm_f(list, noop: nil, verbose: nil) rm list, force: true, noop: noop, verbose: verbose end
Эквивалентно
Bundler::FileUtils.rm(list, force: true)
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 622
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 удаляет файлы list[0] list[1]… Если list[n] является каталогом, удаляет все его содержимое рекурсивно. Этот метод игнорирует StandardError, когда установлен параметр :force.
Bundler::FileUtils.rm_r Dir.glob('/tmp/*')
Bundler::FileUtils.rm_r 'some_dir', force: true
ПРЕДУПРЕЖДЕНИЕ: Этот метод может привести к локальной уязвимости, если один из родительских каталогов или удаляемое дерево каталогов имеют разрешения на запись для всех (включая /tmp, чьи разрешения равны 1777), а текущий процесс обладает сильными привилегиями, такими как суперпользователь Unix (root), и в системе есть символические ссылки. Для безопасного удаления внимательно изучите документацию метода remove_entry_secure и установите параметр :secure в значение true. По умолчанию secure: false.
ПРИМЕЧАНИЕ: Этот метод вызывает remove_entry_secure, если установлен параметр :secure. См. также remove_entry_secure.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 644 def rm_rf(list, noop: nil, verbose: nil, secure: nil) rm_r list, force: true, noop: noop, verbose: verbose, secure: secure end
Эквивалентно
Bundler::FileUtils.rm_r(list, force: true)
ПРЕДУПРЕЖДЕНИЕ: Этот метод может привести к локальной уязвимости. Сначала ознакомьтесь с документацией метода rm_r.
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 263
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 Удаляет один или несколько каталогов.
Bundler::FileUtils.rmdir 'somedir' Bundler::FileUtils.rmdir %w(somedir anydir otherdir) # Does not really remove directory; outputs message. Bundler::FileUtils.rmdir 'somedir', verbose: true, noop: true
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 1137
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 Обновляет время изменения (mtime) и время доступа (atime) файла(ов) в list. Файлы создаются, если они не существуют.
Bundler::FileUtils.touch 'timestamp'
Bundler::FileUtils.touch Dir.glob('*.c'); system 'make'
# File lib/bundler/vendor/fileutils/lib/fileutils.rb, line 155
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 Возвращает true, если new новее всех old_list. Несуществующие файлы старше любого файла.
Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
system 'make hello.o'
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.