есть метод класса (но не метод экземпляра) для ответа на этот вопрос. Метод экземпляра с таким же именем имеется в классе
File::Stat
.
size1 = File.size("file1")
size2 = File.stat("file2").size
Чтобы получить размер файла в блоках, а не в байтах, можно обратиться к методу
blocks
из класса
File::Stat
. Результат, конечно, зависит от операционной системы. (Метод
blksize
сообщает размер блока операционной системы.)
info = File.stat("somefile")
total_bytes = info.blocks * info.blksize
10.1.12.
Опрос специальных свойств файла
У файла есть много свойств, которые можно опросить. Мы перечислим в этом разделе те встроенные методы, для которых не нашлось другого места. Почти все они являются предикатами.
Читая этот раздел (да и большую часть этой главы), помните о двух вещах. Во-первых, так как класс
File
подмешивает модуль
FileTest
, то любую проверку, для которой требуется вызывать метод, квалифицированный именем модуля, можно также выполнить, обратившись к методу экземпляра любого файлового объекта. Во-вторых, функциональность модуля
FileTest
и объекта
File::Stat
(возвращаемого методом
stat
или
lstat
) сильно перекрывается. В некоторых случаях есть целых три разных способа вызвать по сути один и тот же метод. Мы не будем каждый раз приводить все варианты.
В некоторых операционных системах устройства подразделяются на блочные и символьные. Файл может ссылаться как на то, так и на другое, но не на оба сразу. Методы
blockdev?
и
chardev?
из модуля
FileTest
проверяют тип устройства:
flag1 = FileTest::chardev?("/dev/hdisk0") # false
flag2 = FileTest::blockdev?("/dev/hdisk0") # true
Иногда нужно знать, ассоциирован ли данный поток с терминалом. Метод
tty?
класса
IO
(синоним
isatty
) дает ответ на этот вопрос:
flag1 = STDIN.tty? # true
flag2 = File.new("diskfile").isatty # false
Поток может быть связан с каналом (pipe) или сокетом. В модуле
FileTest
есть методы для опроса этих условий:
flag1 = FileTest::pipe?(myfile)
flag2 = FileTest::socket?(myfile)
Напомним, что каталог — это разновидность файла. Поэтому нужно уметь отличать каталоги от обычных файлов, для чего предназначены два метода из модуля
FileTest
:
file1 = File.new("/tmp")
file2 = File.new("/tmp/myfile")
test1 = file1.directory? # true
test2 = file1.file? # false
test3 = file2.directory? # false
test4 = file2.file? # true
В классе
File
есть также метод класса
ftype
, который сообщает вид потока; одноименный метод экземпляра находится в классе
В маске, описывающей режим файла, можно устанавливать или сбрасывать некоторые биты. Они не имеют прямого отношения к битам, обсуждавшимся в разделе 10.1.9. Речь идет о битах set-group-id, set-user-id и бите фиксации (sticky bit).
Для каждого из них есть метод в модуле
FileTest
.
file = File.new("somefile")
info = file.stat
sticky_flag = info.sticky?
setgid_flag = info.setgid?
setuid_flag = info.setuid?
К дисковому файлу могут вести символические или физические ссылки (в тех операционных системах, где такой механизм поддерживается). Чтобы проверить, является ли файл символической ссылкой на другой файл, обратитесь к методу
symlink?
из модуля
FileTest
. Для подсчета числа физических ссылок на файл служит метод
nlink
(он есть только в классе
File::Stat
). Физическая ссылка неотличима от обычного файла — это просто файл, для которого есть несколько имен и записей в каталоге.
File.symlink("yourfile","myfile") # Создать ссылку
is_sym = FileTest::symlink?("myfile") # true
hard_count = File.new("myfile").stat.nlink # 0
Отметим попутно, что в предыдущем примере мы воспользовались методом класса
symlink
из класса
File
для создания символической ссылки.
В редких случаях может понадобиться информация о файле еще более низкого уровня. В классе
File::Stat
есть еще три метода экземпляра, предоставляющих такую информацию. Метод
dev
возвращает целое число, идентифицирующее устройство, на котором расположен файл. Метод
rdev
возвращает целое число, описывающее тип устройства, а для дисковых файлов метод
ino
возвращает номер первого индексного узла, занятого файлом.
file = File.new("diskfile")
info = file.stat
device = info.dev
devtype = info.rdev
inode = info.ino
10.1.13. Каналы
Ruby поддерживает разные способы читать из канала и писать в него. Метод класса
IO.popen
открывает канал и связывает с возвращенным объектом стандартные ввод и вывод процесса. Часто с разными концами канала работают разные потоки, но в примере ниже запись и чтение осуществляет один и тот же поток:
check = IO.popen("spell","r+")
check.puts("'T was brillig, and the slithy toves")
check.puts("Did gyre and gimble in the wabe.")
check.close_write
list = check.readlines
list.collect! { |x| x.chomp }
# list равно %w[brillig gimble gyre slithy toves wabe]
Отметим, что вызов
close_write
обязателен, иначе мы никогда не достигнем конца файла при чтении из канала. Существует также блочная форма:
File.popen("/usr/games/fortune") do |pipe|
quote = pipe.gets
puts quote
# На чистом диске можно искать бесконечно. – Том Стил.
end
Если задана строка
"-"
, то запускается новый экземпляр Ruby. Если при этом задан еще и блок, то он работает в двух разных процессах, как в результате разветвления (fork); блоку в процессе-потомке передается
nil
, а в процессе-родителе — объект
IO
, с которым связан стандартный ввод или стандартный вывод.