- Работа
- Linux
- Monitoring
- Мониторинг каталога на диске
Мониторинг каталога на диске
Скрипт выведет на экран информацию о том какие файлы или каталоги были изменены. К сожалению копирование, переименование или перенос файла будут отображаться как удаление старого и создание нового. Что бы следить на таком уровне нужен механизм немного другого типа, например inode.
Ниже представлено простое решение. Достаточно добавить запуск скрипта в крон и получать уведомления если код изменился...
Первый параметр это каталог за которым мониторим. Второй временный файл со всеми хэшами и далее каталоги с исключениями, на которые мы не смотрим...
crontab -e
0 * * * * /srv/scrips/check_code.sh /srv/www/crypto/ /tmp/cypto.md5 .git | /srv/scrips/send_notify.sh -p 5
check_code.sh
#!/usr/bin/env bash
# check_code.sh
# Сравнение MD5 по дереву каталога с прошлым запуском.
# usage: check_code.sh <каталог> <файл_манифеста> [исключения...]
#
# Манифест — путь относительно <каталог>: файлы — «путь<TAB>md5», подкаталоги — «путь<TAB>DIR» (без хэша).
# Если манифест старый (только файлы), удали его один раз, иначе все каталоги дадут «добавлен (каталог)».
# Исключения (пути относительно <каталог> из 1-го аргумента):
# для каждого аргумента: -not -path "$DIR/путь" -not -path "$DIR/путь/*"
# примеры: log, var/cache, node_modules, .git
set -euo pipefail
usage() {
echo "Usage: $0 <directory> <manifest_file> [exclude ...]" >&2
echo " manifest_file — файл path<TAB>md5 и path<TAB>DIR (каталоги) между запусками" >&2
echo " exclude — путь от корня проекта: log, var/cache, node_modules" >&2
}
DIR="${1:-}"
MANIFEST_FILE="${2:-}"
shift 2 || true
# Проверка аргументов командной строки
if [[ -z "$DIR" || -z "$MANIFEST_FILE" ]]; then
usage
exit 2
fi
# Проверка существования каталога
if [[ ! -d "$DIR" ]]; then
echo "Каталог не найден: $DIR" >&2
exit 2
fi
# Получение абсолютного пути каталога
DIR="$(cd "$DIR" && pwd)"
# Одинаковые исключения для файлов и каталогов
EXCLUDE=()
for pat in "$@"; do
EXCLUDE+=(-not -path "${DIR}/${pat}" -not -path "${DIR}/${pat}/*")
done
FIND_FILES=(find "$DIR" -type f "${EXCLUDE[@]}")
# mindepth 1: сам корень проекта в манифест не пишем — только подкаталоги
FIND_DIRS=(find "$DIR" -mindepth 1 -type d "${EXCLUDE[@]}")
TMP_NEW="$(mktemp)"
# Удаление временного файла при завершении скрипта
trap 'rm -f -- "$TMP_NEW"' EXIT
# Сбор манифеста: rel<TAB>md5 | rel<TAB>DIR. Сортировка в конце по пути
{
"${FIND_FILES[@]}" -print0 | while IFS= read -r -d '' f; do
rel="${f#$DIR/}"
sum=$(md5sum -- "$f" 2>/dev/null | awk '{print $1}')
printf '%s\t%s\n' "$rel" "$sum"
done
"${FIND_DIRS[@]}" -print0 | while IFS= read -r -d '' d; do
rel="${d#$DIR/}"
printf '%s\tDIR\n' "$rel"
done
} | LC_ALL=C sort -t $'\t' -k1,1 > "$TMP_NEW"
diff_report() {
local oldf="$1" newf="$2"
awk -F '\t' '
FNR == NR { if (NF >= 2) { old[$1] = $2 } next }
NF < 2 { next }
{
p = $1; m = $2
if (p in old) {
if (old[p] != m) print "изменён: " p
delete old[p]
} else {
if (m == "DIR") print "добавлен каталог: " p
else print "добавлен файл: " p
}
}
END {
for (p in old) {
if (old[p] == "DIR") print "удалён каталог: " p
else print "удалён файл: " p
}
}
' "$oldf" "$newf"
}
# Первый запуск — только записать манифест, без уведомления
if [[ ! -f "$MANIFEST_FILE" ]]; then
cp -f -- "$TMP_NEW" "$MANIFEST_FILE"
chmod 600 -- "$MANIFEST_FILE" 2>/dev/null || true
exit 0
fi
# Сравнение с предыдущим манифестом
if cmp -s -- "$MANIFEST_FILE" "$TMP_NEW"; then
exit 0
fi
REPORT=$(diff_report "$MANIFEST_FILE" "$TMP_NEW" || true)
echo "Код проекта \"$DIR\" был изменен"
printf '%s\n' "$REPORT"
cp -f -- "$TMP_NEW" "$MANIFEST_FILE"
chmod 600 -- "$MANIFEST_FILE" 2>/dev/null || true
exit 0
[ Править ]
