GitHub

Tensho

Заметки непутевого программиста

Немножко о файлах в экосистеме Ruby

21/08/2018

После обильной теории в “Современные операционные системы” Таненбаума, книга “Advanced Programming in the UNIX Environment” (W. Richard Stevens, Stephen A. Rago) является отличным практическим дополнением к ней. Глава 4 “Files and Directories” вдохновила меня порыться в стандартной библиотеке Ruby и написать пару незамысловатых скриптов, которые прочесывают файловую систему и аггрегируют сущности файловой системы по владельцу (UID, GID) или типу (обычный файл, символьная ссылка, канал и т.д.). В прикладных Ruby программах редко приходится идти далее всем известных File.open, File.read, File.write и File.join. Однако, если порыться глубже, то можно найти практически все аналоги POSIX библиотечных функций, которые описаны в книге. Так модуль File::Stat предоставляет информацию о владельце, правах доступа, времени доступа/изменения данных/изменения атрибутов и д.р. Модуль FileTest позволяет проверить тип файла и всевозможные биты режима (setuid, setgid, sticky, readable, writable, executable). Настоящей находкой для меня оказались модули Find и Etc, которые позволяют легко делать траверс иерархии директорий и запрашивать разного рода информацию из системных конфигурационных папок (/etc/passwd, /etc/group) соответственно. Также стоит упомянуть широкоизвестный класс Dir, т.к. некоторые библиотечных функций C дря работы с директориями описанные в книге можно найти только здесь – создать/удалить директорию, узнать текущую директорию, сменить текущую директорию, вычитать все объекты заданной директории по шаблону и д.р.

Пример 1. Аггрегирование файлов по владельцам и группам.

# files-by-user.rb
require "find"
require "etc"
require "awesome_print"

files = {
  users: Hash.new(0),
  groups: Hash.new(0)
}

Find.find(ARGV[0]) do |path|
  if FileTest.file?(path)
    stat = File.stat(path)
    uid, gid = stat.uid, stat.gid
    name, group = Etc.getpwuid(uid).name, Etc.getgrgid(gid).name
    files[:users][name] += 1
    files[:groups][group] += 1
  end
end

ap files
$ ruby files-by-user.rb /usr/local
{
     :users => {
          "root" => 155,
        "tensho" => 15966
    },
    :groups => {
        "wheel" => 153,
        "admin" => 14067,
        "staff" => 1901
    }
}

Пример 2. Аггрегирование файлов по типам.

# files-by-type.rb
require "find"
require "awesome_print"

files = Hash.new(0)

Find.find(ARGV[0]) do |path|
  files[:blockdev] += 1 if FileTest.blockdev?(path)
  files[:chardev] += 1 if FileTest.chardev?(path)
  files[:directory] += 1 if FileTest.directory?(path)
  files[:executable] += 1 if FileTest.executable?(path)
  files[:file] += 1 if FileTest.file?(path)
  files[:pipe] += 1 if FileTest.pipe?(path)
  files[:socket] += 1 if FileTest.socket?(path)
  files[:symlink] += 1 if FileTest.symlink?(path)
  files[:zero] += 1 if FileTest.zero?(path)
end

ap files
$ ruby files-by-type.rb /usr/local
{
     :directory => 913,
    :executable => 1330,
          :file => 16121,
          :zero => 17,
       :symlink => 3111
}