Немножко о файлах в экосистеме 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
}