Я бы использовал Python для такой задачи. Это может привести к большему количеству кода, чем чистое решение bash, но:
- это (IMO) проще проверить, просто используйте
питест
или же объединяться
модуль
- это читается для людей, не использующих Linux (ну, за исключением
get_device
функция, специфичная для Linux...)
- легче начать (опять же IMO)
- Что делать, если вы хотите отправить несколько писем? Чтобы вызвать новые действия? Скрипты можно легко дополнить с помощью такого языка программирования, как Python.
Начиная с Python 3.3, шутил
модуль поставляется с функцией с именем disk_usage
. Его можно использовать для получения информации об использовании диска на основе заданного каталога.
Незначительная проблема заключается в том, что я не знаю, как легко получить имя диска, т.е. /dev/sdb
, даже несмотря на то, что можно получить сведения об использовании его диска (используя любой каталог, смонтированный на /dev/sdb
, в моем случае $ГЛАВНАЯ
Например). Я написал функцию под названием get_device
для этой цели.
#!/usr/bin/env python3
импортировать аргументы
из os.path импортировать getmtime
из Shutil импортировать disk_usage, rmtree
из системного импорта выход
из пути импорта pathlib
от ввода импорта Iterator, Tuple
def get_device (путь: путь) -> ул:
"""Найти монтирование для данного каталога. Это необходимо только для ведения журнала."""
# Прочтите /etc/mtab, чтобы узнать о точках монтирования
mtab_entries = Путь("/etc/mtab").read_text().splitlines()
# Создаем список точек монтирования и устройств
mount_points = dict([list(reversed(line.split(" ")[:2])) для строки в mtab_entries])
# Находим точку монтирования заданного пути
в то время как path.resolve(True).as_posix() не находится в mount_points:
путь = путь.родитель
# Возвращаем устройство, связанное с точкой монтирования
вернуть mount_points[path.as_posix()]
def get_directory_and_device(path: str) -> Tuple[str, Path]:
"""Выйти из процесса, если каталог не существует."""
fs_path = Путь (путь)
# Путь должен существовать
если не fs_path.exists():
print(f"ОШИБКА: Нет такого каталога: {путь}")
выход(1)
# И путь должен быть допустимым каталогом
если не fs_path.is_dir():
print(f"Путь должен быть каталогом, а не файлом: {путь}")
выход(1)
# Получить устройство
устройство = get_device (fs_path)
возвращаемое устройство, fs_path
def get_disk_usage(path: Path) -> float:
# Shutil.disk_usage поддерживает путь как объекты, поэтому нет необходимости приводить к строке
использование = использование_диска (путь)
# Получить использование диска в процентах
вернуть использование.использовано/использование.всего * 100
def remove_file_or_directory(path: Path) -> None:
"""Удалить указанный путь, который может быть каталогом или файлом."""
# Удалить файлы
если path.is_file():
путь.отключить()
# Рекурсивно удалить деревья каталогов
если path.is_dir():
rmtree (путь)
определение find_oldest_files(
путь: путь, шаблон: str = "*", порог: int = 80
) -> Итератор[Путь]:
"""Перебрать файлы или каталоги, присутствующие в каталоге, которые соответствуют заданному шаблону."""
# Перечислить файлы в каталоге, полученном в качестве аргумента, и отсортировать их по возрасту
файлы = отсортированные (path.glob (шаблон), key = getmtime)
# Выдавать пути к файлам до тех пор, пока использование не станет ниже порогового значения
для файла в файлах:
использование = get_disk_usage (путь)
если использование < порог:
сломать
выходной файл
защита check_and_clean(
путь: ул,
порог: интервал = 80,
удалить: логическое значение = ложь,
) -> Нет:
"""Основная функция"""
устройство, fspath = get_directory_and_device(путь)
# Shutil.disk_usage поддерживает путь как объекты, поэтому нет необходимости приводить к строке
использование = использование_диска (путь)
# Примите меры, если это необходимо
если использование > порог:
Распечатать(
f"Использование диска превышает пороговое значение: {usage:.2f}% > {threshold}% ({device})"
)
# Перебираем файлы для удаления
для файла в find_oldest_files(fspath, "*", порог):
print(f"Удаление файла {файл}")
если удалить:
remove_file_or_directory(файл)
def main() -> Нет:
синтаксический анализатор = argparse.ArgumentParser(
description="Удалить старые файлы, когда использование диска превышает лимит."
)
parser.add_argument(
"path", help="Путь к каталогу, в котором должны быть удалены файлы", type=str
)
parser.add_argument(
"--порог",
"-т",
метавар = "Т",
help="Порог использования в процентах",
тип = целое,
по умолчанию=80,
)
parser.add_argument(
"--Удалить",
"--рм",
help="Файлы не удаляются, если не указан параметр --removed или --rm",
действие = "store_true",
по умолчанию = Ложь,
)
аргументы = парсер.parse_args()
check_and_clean(
аргументы.путь,
порог=args.threshold,
удалить=args.remove,
)
если __name__ == "__main__":
главный()
Если вам нужно организовать множество задач с помощью CRON, возможно, стоит собрать некоторый код Python в виде библиотеки и повторно использовать этот код во многих задачах.
РЕДАКТИРОВАТЬ: я, наконец, добавил часть CLI в сценарий, думаю, я буду использовать ее сам.