Przejdź do treści

Monitorowanie zmian w katalogu i uruchamianie skryptu przy pomocy (systemd.path i inotifywait)


systemd.path i inotifywait

W wielu przypadkach przydaje się możliwość monitorowania zmian w katalogu, np. aby:

  • wysyłać powiadomienie na Discorda
  • wykonywać backup po zmianie
  • uruchamiać automatyczne skrypty CI/CD
  • integrować z systemami monitoringu

Przedstawię tutaj dwie metody:

1️⃣ systemd.path — prosty monitoring katalogu
2️⃣ inotifywait — pełny monitoring rekurencyjny


systemd.path — prosta metoda dla katalogu głównego

systemd od wersji 215 posiada typ unitów .path, które pozwalają na monitorowanie pliku lub katalogu. W momencie tworzenia tego wpisu systemd jest w wersji 257, zatem funkcjonalność jest stabilna.

Jak to działa?

  • PathModified=/path - wykrywa zmianę w danym katalogu (np. dodanie/ usunięcie pliku lub podkatalogu)
  • nie monitoruje rekurencyjnie - nie zobaczy zmian wewnątrz podkatalogów

Note

PathModified monitoruje zmianę samego katalogu, czyli np.:

  • utworzenie nowego pliku
  • usunięcie pliku
  • utworzenie nowego podkatalogu
  • usunięcie podkatalogu

Nie wykryje natomiast zmian w plikach wewnątrz podkatalogów.

Przykład: katalog bez podkatalogów

Załóżmy mamy katalog /var/www/html/.

W nim znajdują się np.:

/var/www/html/
├── file1
├── file2
└── file3

Implementacja krok po kroku

Skrypt powiadamiający

Plik /usr/local/bin/discord_notify.sh:

#!/bin/bash

source ~/.bashrc
directory="/var/www/html"

curl -H "Content-Type: application/json" -X POST -d "{\"content\": \"Change detected in $directory\"}" "$DISCORD_WEBHOOK_URL"
Ustawienie zmiennej środowiskowej
Ustaw zmienną środowiskową `DISCORD_WEBHOOK_URL` w pliku `~/.bashrc` lub innym odpowiednim miejscu.
W przypadku zmiennej środowiskowej, możesz wykorzystać również inne pliki/metody, bashrc nie zawsze może się sprawdzić.
Jeśli użyjesz w konfiguracji unit'a EnvironmentFile, bashrc nie będzie odczytany z uwagi na sposób w jaki systemd uruchamia usługi.
Linux daje tutaj wiele możliwości, zmienne można definiowac na serwerza jak i w bardziej złożonych systemach, w konfiguracji GitHub Actions, GitLab CI/CD, Jenkins, itp.
Przedstawiony przykład jest jednym z wielu sposobów.

Nadajemy uprawnienia:

chmod +x /usr/local/bin/discord_notify.sh

Unit .service

Plik /etc/systemd/system/discord-notify.service:

[Unit]
Description=Sending Discord notification on changes in directory

[Service]
Type=oneshot
ExecStart=/usr/local/bin/discord_notify.sh

[Install]
WantedBy=multi-user.target

Unit .path

Plik /etc/systemd/system/discord-notify.path:

[Unit]
Description=Checking changes in /var/www/html

[Path]
PathModified=/var/www/html

[Install]
WantedBy=multi-user.target

Uruchomienie

systemctl daemon-reload
systemctl enable --now discord-notify.path

Test

systemd.path monitoring

Przykładowe operacje i wynik:

Operacja Skrypt się uruchomi?
touch /var/www/html/file1 ✅ TAK
rm /var/www/html/file1 ✅ TAK
mkdir /var/www/html/dirxx ✅ TAK
touch /var/www/html/dirxx/file1 ❌ NIE
rm /var/www/html/dirxx/file1 ❌ NIE

Warning

**`systemd.path` nie monitoruje zmian wewnątrz podkatalogów.**
Zmiana w `/var/www/html/dirxx/file.txt` nie uruchomi skryptu.

Jak działa PathExistsGlob

Dyrektywa PathExistsGlob pozwala określić wzorzec (glob), który systemd będzie sprawdzać. Jeśli co najmniej jeden plik lub katalog pasuje do wzorca, to powiązany .service zostanie uruchomiony.

Mechanizm działa jak funkcja glob() w bashu:

  • * — dowolny ciąg znaków (ale tylko jeden poziom katalogu)
  • np. /opt/www/* — wszystko w katalogu /opt/www/ (przy założeniu, że katalog jest pusty)
  • np. /backup/*.tar.gz — wszystkie pliki .tar.gz w katalogu /backup/

Ograniczenia

  • PathExistsGlob nie śledzi zmian w plikach (np. modyfikacji ich zawartości) — tylko sprawdza, czy pasujący plik lub katalog istnieje.
  • Wzorce rekurencyjne typu ** (np. /opt/www/**/*.txt) nie są obsługiwane w systemd.path. Jeśli potrzebujesz śledzenia zmian rekurencyjnych, lepszym wyborem jest inotifywait.

Przykład użycia

Załóżmy, że mamy katalog /var/incoming/, do którego proces lub skrypt wrzuca pliki .done.

[Unit]
Description=Check if .done exists in /var/incoming

[Path]
PathExistsGlob=/var/incoming/*.done

[Install]
WantedBy=multi-user.target

Gdy tylko pojawi się nowy plik *.done, np.:

/var/incoming/dataset_2025_06_07.done

uruchomi się powiązany .service.

Inny przykład — katalog /var/www/

PathExistsGlob=/opt/www/*

Uruchomi .service w momencie, gdy w katalogu /opt/www/ pojawi się:

  • dowolny plik, np. index.html
  • dowolny katalog, np. images/

PathExistsGlob=/opt/www/* działa tylko na bezpośrednie podkatalogi folderu /var/www/. Zmiany w podkatalogach (/opt/www/images/photo.jpg) nie są bezpośrednio śledzone - jeżeli podkatalog powstanie (mkdir images), to zadziała.

Podsumowanie

PathExistsGlob to szybki sposób na reagowanie na pojawienie się plików lub katalogów:

  • ✅ proste monitorowanie katalogu
  • ✅ reagowanie na nowe pliki
  • ❌ brak wsparcia dla pełnej rekurencji
  • ❌ brak monitorowania modyfikacji lub usunięcia plików

Dla pełnego rekurencyjnego śledzenia katalogu i zmian w plikach lepszym wyborem będzie narzędzie inotifywait.


inotifywait - monitoring rekurencyjny

Jeżeli chcesz monitorować:

  • katalog + podkatalogi
  • dowolne zmiany w plikach
  • utworzenie/usunięcie folderów i plików

systemd.path nie wystarczy. Potrzebne będzie narzędzie: inotifywait.

Instalacja

apt install inotify-tools

lub

yum install inotify-tools

Skrypt monitorujący

Plik /usr/local/bin/discord_watch.sh:

#!/bin/bash

WATCH_DIR="/var/www/html"

inotifywait -mrq -e close_write,move,create,delete "$WATCH_DIR" | while read -r path action file; do

curl -H "Content-Type: application/json" -X POST -d "{\"content\": \"Change: ${action} -> ${path}${file}\"}" "$DISCORD_WEBHOOK_URL"

done

Nadajemy uprawnienia:

chmod +x /usr/local/bin/discord_watch.sh

Unit systemd dla discord_watch.sh

Plik /etc/systemd/system/discord-watch.service:

[Unit]
Description=Recursive monitoring of /var/www/html
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/discord_watch.sh
Restart=always

[Install]
WantedBy=multi-user.target

Uruchomienie

systemctl daemon-reload
systemctl enable --now discord-watch.service

Test

systemd.path monitoring

Przykładowe operacje i wynik:

Operacja Skrypt się uruchomi?
touch /var/www/html/new_file ✅ TAK
rm /var/www/html/file1 ✅ TAK
mkdir /var/www/html/dir1 ✅ TAK
touch /var/www/html/dir1/file1 ✅ TAK
rm /var/www/html/dir1/file1 ✅ TAK

Note

`inotifywait` pozwala na pełny monitoring rekurencyjny katalogu i wszystkich jego podkatalogów.
Możemy monitorować dowolne zdarzenia, np.: `create`, `delete`, `move`, `close_write`, `attrib` (zmiana metadanych).

Podsumowanie

Cecha systemd.path inotifywait + systemd.service
Wbudowany w systemd ❌ (wymaga inotify-tools)
Rekurencja (podkatalogi) ❌ NIE ✅ TAK
Monitorowanie zmian w plikach ✅ TAK ✅ TAK
Monitorowanie zmian w podkatalogach ❌ NIE ✅ TAK
Wydajność bardzo dobra bardzo dobra

Kiedy co stosować?

  • systemd.path - jeśli katalog nie ma podkatalogów lub interesuje nas tylko sama struktura katalogu
  • inotifywait - jeżeli potrzebujemy pełnego monitorowania całego drzewa katalogów (np. /var/www/html lub /opt/data etc.)

Uwagi praktyczne

  • Edytory plików (np. vim, nano) mogą powodować tymczasowe operacje, które będą widziane jako create i move. Warto w takich przypadkach ograniczyć typy eventów lub stosować prosty filtr w skrypcie.
  • inotifywait działa bardzo szybko i jest mało zasobożerne. W przypadku dużych katalogów warto testować wydajność.