[Перевод] Кунг-фу стиля Linux: наблюдение за файловой системой
Пользователи UNIX-подобных ОС, чтобы достичь желаемого результата, привыкли организовывать совместную работу различных программ, рассчитанных на решение какой-то одной задачи. Эти команды, например, помещают в файл Bash-скрипта, а потом, по своей инициативе, вызывают этот скрипт из командной строки. А что если нужно, чтобы система сама реагировала бы на какие-то изменения, действуя без вмешательства пользователя? Например, нужно организовать наблюдение за директорией и автоматически вызывать какую-то программу тогда, когда в эту директорию будет скопирован файл с использованием FTP. При этом не хотелось бы сидеть за компьютером во время передачи этого файла и ждать завершения операции.
Самый простой, но не очень-то красивый способ это сделать заключается в периодическом сканировании директории. Вот весьма примитивный скрипт, который реализует такой функционал:
#!/bin/bash
while true
do
for I in `ls`
do cat $I; rm $I
done
sleep 10
done
В этом примере я вывожу содержимое файла в консоль и удаляю его. Но в реальной жизни с подобным файлом можно сделать что-нибудь куда более интересное. Это — пример того, как не стоит писать скрипты. Ведь скрипт всё время выполняется. К тому же, такое решение задачи нельзя назвать красивым или остроумным. (Если вы полагаете, что в данном примере мне стоило бы использовать конструкцию for I in *
— попробуйте это сделать в пустой директории и вы поймёте причину использования команды ls
.)
Как организовать наблюдение за файловой системой в Linux?
Более симпатичное решение задачи
Если говорить честно, то для решения нашей задачи хорошо было бы сделать нечто, выглядящее лучше вышеприведённого примера. В современных ядрах (начиная с 2.6.13) есть механизм уведомлений о событиях файловой системы, представленный интерфейсом inotify
. Соответствующие вызовы можно использовать программно, применяя заголовочный файл sys/inotify.h
. Существует и набор инструментов командной строки, который можно установить, обычно представленный пакетом inotify-tools
.
Один из инструментов этого пакета, inotifywait
, позволяет улучшить код нашего примера. Скажем, переделать код можно так:
#!/bin/bash
while true
do
if FN=`inotifywait –e close_write,moved_to --format %f .`
then
cat $FN
rm $FN
fi
done
Я думаю, что этот вариант лучше предыдущего. Скрипт вызывается не через заданные промежутки времени, а только тогда, когда в файловой системе что-то произошло. Я полагаю, что любая нормальная программа, воздействующая на файлы, находящиеся в директории, либо открывает файлы для записи и закрывает их после завершения операции, либо перемещает файлы в директорию. Наша программа отреагирует на оба эти события, а благодаря %f
команда сообщит имя файла. Есть, конечно, и другие события, за которыми можно наблюдать.
Если вас интересует вопрос о том, почему необходимо наблюдать за событием перемещения файла, подумайте о том, как работает большинство текстовых редакторов и программ для загрузки файлов по сети. Обычно новому файлу не дают постоянного имени до тех пор, пока не завершится его обработка. Например, Chrome будет загружать файл с именем test.txt
, дав ему временное имя test.txt.crdownload
(или имя, подобное этому). Файл будет переименован (перемещён) в файл test.txt
только после завершения загрузки.
Если вы хотите попробовать команду inotifywait
без написания скрипта, чтобы своими глазами увидеть то, как она работает, откройте пару окон терминала и воспроизведите в этих окнах то, что показано ниже.
Исследование команды inotifywait
В окне, расположенном на заднем плане, выполните команду inotifywait
. Не забудьте о точке в конце команды, которая указывает на то, что наблюдать надо за файлами в текущей директории. Затем, в другом окне терминала, создайте в той же самой директории файл. В окне первого терминала будет выведено имя файла, после чего inotifywait
завершит работу. В скрипте используется тот же самый механизм, с его помощью скрипт записывает имя файла в переменную FN
, выполняет некие действия и перезапускает inotifywait
. Кстати, можно сделать так, чтобы утилита inotifywait
, после срабатывания, не завершала бы работу, но это усложнит скрипт. Это, правда, устранит проблему, которая может возникнуть в том случае, если имя файла меняется в ходе попытки его обработать средствами скрипта.
Ещё один инструмент для наблюдения за файловой системой, inotifywatch
, тоже реагирует на изменения файлов, но он осуществляет мониторинг файловой системы в течение некоторого времени, а потом выдаёт сводку по зафиксированным изменениям. Если вы полагаете, что это именно то, что вам нужно, почитайте справку по inotifywatch
.
Новый cron
Хотя мы и улучшили наш скрипт, он всё ещё далёк от идеала. Вполне возможно то, что нужно организовать наблюдение за множеством директорий. Вряд ли кому-то захочется воспроизводить вышеописанную последовательность действий в применении к каждой интересующей его директории.
Для решения нашей задачи можно воспользоваться и ещё одной программой, называемой incron
(я почти уверен в том, что вам эта программа пригодится, и что её стоит установить). Эта утилита похожа на cron
, но вместо того, чтобы выполнять какие-то действия, ориентируясь на события, связанные с временем, она ориентируется на события, связанные с файлами. После её установки вам, вероятно, если вы планируете ей пользоваться, стоит отредактировать файлы /etc/incron.allow
и /etc/incron.deny
. Особенно — если вы будете применять её, работая как обычный пользователь.
Предположим, нам надо выполнять некий скрипт в том случае, если в директории hexfiles
появится какой-нибудь файл. Для редактирования таблицы событий, за которыми наблюдает incron
, можно воспользоваться командой incrontab -e
. Данные, вносимые в эту таблицу, представляют собой описания задач и по-особенному форматируются. В частности, тут для разделения столбцов используются не знаки табуляции, а пробелы. Вот строка, которая позволяет решить вышеописанную задачу:
/home/alw/Downloads/hexfiles IN_CLOSE_WRITE,IN_MOVED_TO /home/alw/bin/program_cpu $@/$#
Конструкция $@/$#
в конце этой строки позволяет получить полный путь к файлу, на событие, произошедшее с которым, отреагировала программа. Тут, кроме того, можно получить время возникновения события — либо в виде текста ($%
), либо в виде числа ($&
). С помощью incron
можно наблюдать за обычными событиями. Утилита, кроме того, поддерживает различные опции. Например, можно не разыменовывать символические ссылки. Подробности об incron
ищите в справке к утилите.
Настройка incron с использованием графического интерфейса
Я — не большой любитель Linux-инструментов с графическим интерфейсом, но я знаю, что многим они нравятся. Если вы — из их числа — вот incrontab-редактор, написанный на Java. Нельзя сказать, что этот проект отличается подробной документацией, но его использование упрощает то, что в него можно импортировать файл incrontab
. Если он у вас есть — ищите его по пути /var/spool/incron/your_user_id
. Если посмотреть на окно редактора, показанное ниже, можно заметить, что он позволяет упростить создание incron-таблицы.
Графический интерфейс для incron
Обычно системные файлы можно найти в /etc/incron.d
. Пути расположения файлов можно задавать в файле /etc/incron.conf
. Поэтому, если вам нужно поменять место расположения файла таблицы, но вы не знаете о том, где его искать, загляните в этот файл.
Итоги
Использование incron
выглядит как очень аккуратное решение задачи мониторинга файловой системы. Системная программа берёт на себя заботу о наблюдении за событиями файловой системы, а скрипт запускается лишь тогда, когда это нужно. Кроме того, используя эту программу, можно легко узнать о том, за чем именно организовано наблюдение. В результате incron
— это инструмент, с помощью которого можно сделать куда больше, чем наблюдение за текущей папкой в открытом окне терминала.
Планируете ли вы пользоваться incron?