С вопросом о том, существует ли файл, связан другой вопрос: а есть ли в нем какие-нибудь данные? Ведь файл может существовать, но иметь нулевую длину — а это практически равносильно тому, что он отсутствует.
Если нас интересует только, пуст ли файл, то в классе File::Stat есть два метода экземпляра, отвечающих на этот вопрос. Метод zero? возвращает true, если длина файла равна нулю, и false в противном случае.
flag = File.new('somefile').stat.zero?
Метод size? возвращает либо размер файла в байтах, если он больше нуля, либо nil для файла нулевой длины. Не сразу понятно, почему nil, а не 0. Дело в том, что метод предполагалось использовать в качестве предиката, а значение истинности нуля в Ruby — true, тогда как для nil оно равно false.
if File.new('myfile').stat.size?
puts 'В файле есть данные.'
else
puts 'Файл пуст.'
end
Методы zero? и size? включены также в модуль FileTest:
flag1 = FileTest::zero?('file1')
flag2 = FileTest::size?('file2')
Далее возникает следующий вопрос: «Каков размер файла?» Мы уже видели что для непустого файла метод size? возвращает длину. Но если мы применяем его не в качестве предиката, то значение nil только путает.
В классе File есть метод класса (но не метод экземпляра) для ответа на этот вопрос. Метод экземпляра с таким же именем имеется в классе 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, который сообщает вид потока; одноименный метод экземпляра находится в классе File::Stat. Этот метод возвращает одну из следующих строк: file, directory, blockSpecial, characterSpecial, fifo, link или socket (строка fifо относится к каналу).
this_kind = File.ftype('/dev/hdisk0') # 'blockSpecial'
that_kind = File.new('/tmp').stat.ftype # 'directory'
В маске, описывающей режим файла, можно устанавливать или сбрасывать некоторые биты. Они не имеют прямого отношения к битам, обсуждавшимся в разделе 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')
