Puncte:18

Ubuntu șterge automat cel mai vechi fișier din director când discul depășește 90% capacitatea, repetă până când capacitatea sub 80%

drapel bv

Am găsit câteva scripturi de job cron similare, dar nimic exact cum am nevoie de ele și nu știu suficient despre scripting pentru Linux pentru a încerca să modific codul atunci când vine vorba de acest tip de job, care ar putea deveni dezastruos.

În esență, am camere ip care înregistrează /home/ben/ftp/surveillance/ dar trebuie să mă asigur că există întotdeauna suficient spațiu pe disc pentru a face acest lucru.

Ar putea cineva să mă îndrume cum pot configura o lucrare cron pentru:

Verifica daca /dev/sbd/ a ajuns la 90% capacitate. Dacă da, ștergeți cel mai vechi fișier din (și fișierele din subdosare) /home/ben/ftp/surveillance/ Și repetă asta până când /dev/sbd/ capacitatea este sub 80% Repetați la fiecare 10 minute.

J... avatar
drapel in
Ce se întâmplă dacă `/dev/sdb` (notă: `sdb` != `sbd` - urmăriți greșelile de scriere în scripturile dvs.!) devine vreodată >80% plin, indiferent de conținutul `/home/ben/ftp/surveillance/` ? Aceasta vă va șterge întotdeauna toate înregistrările, fără să vă lăsați nimic. Mai bine să aveți camere CCTV care înregistrează la un volum dedicat care nu este partajat cu (adică) sistemul dvs. de operare sau cu alți utilizatori. În mod ideal, camerele ar trebui să gestioneze singure acest lucru, fiind conștienți de ce fișiere aparțin lor și suprascriind propriile fișiere cele mai vechi atunci când spațiul dedicat devine plin.
drapel bv
Bun punct J... Directorul meu ftp este de fapt o unitate externă montată în directorul meu de acasă și acum este dedicat doar înregistrărilor cu camere. Nu m-am obosit să schimb structura de foldere pe care o are înainte de a fi un disc dedicat. Camerele încarcă, de asemenea, o fotografie .jpg din fiecare înregistrare, așa că voi configura un alt cron pentru a șterge periodic toate .jpg.
Puncte:33
drapel in

Scrierea acestor tipuri de scenarii pentru oameni mă face întotdeauna nervos pentru că, în cazul în care ceva nu merge bine, se va întâmpla unul dintre cele trei lucruri:

  1. Mă voi da cu piciorul pentru ceea ce este probabil o greșeală de tip n00b
  2. Amenințările cu moartea îmi vor veni în cale pentru că cineva a copiat/lipit orbește fără:
    • făcând un efort pentru a înțelege scenariul
    • testarea scriptului
    • având o rezervă rezonabilă
  3. Toate cele de mai sus

Așadar, pentru a reduce riscul tuturor celor trei, iată un kit de pornire pentru tine:

#!/bin/sh
DIR=/home/ben/ftp/surveillance
ACT=90
df -k $DIR | grep -vE '^Sistem de fișiere' | awk '{ print $5 " " $1 }' | în timpul citirii ieșirii;
do
  echo $ieșire
  usep=$(echo $ieșire | awk '{ print $1}' | cut -d'%' -f1 )
  partiție=$(echo $ieșire | awk '{ print $2 }' )
  if [ $usep -ge $ACT ]; atunci
    echo „Epuizarea spațiului \”$partiția ($usep%)\” pe $(nume gazdă) ca la $(data)”
    fişier vechi=$(ls -dltr $DIR/*.gz|awk '{ print $9 }' | head -1)
    echo "Să ștergem \"$oldfile\" ..."
  fi
Terminat

LUCRURI DE REȚINUT:

  1. Acest script nu șterge nimic

  2. DIR este directorul cu care să lucrezi

  3. ACT este procentul minim necesar pentru a acționa

  4. Doar un fișier â cel mai vechi â este selectat pentru „ștergere”

  5. Veți dori să înlocuiți *.gz cu tipul de fișier real al videoclipurilor dvs. de supraveghere.
    NU FOLOSI *.* SAU * DE LA SINE!

  6. Dacă partiția care conține DIR este la o capacitate mai mare decât ACT, veți vedea un mesaj ca acesta:

    97% /dev/sda2
    Epuizarea spațiului „/dev/sda2 (97%)” pe ubuntu-vm la miercuri, 12 ianuarie, 07:52:20 UTC 2022
    Să ștergem „/home/ben/ftp/surveillance/1999-12-31-video.gz”...
    

    Din nou, acest scenariu nu voi sterge orice.

  7. Dacă sunteți mulțumit de rezultat, puteți continua să modificați scriptul pentru a șterge/muta/arhiva după cum credeți de cuviință

Testați des. Testează bine. Și amintiți-vă: Când puneți rm într-un script, nu există nicio anulare.

drapel ao
Este o prostie să transferați `grep` la `awk`, puteți doar să adăugați `!/^Filesystem/` la începutul comenzii awk. Sau îi puteți spune lui `df` să producă numai rezultatul dorit și să folosiți `sed` pentru a elimina antetul și semnul procentual: `usep=$(df --output=pcent $DIR | sed '1d;s/%/ /')`
Peter Cordes avatar
drapel fr
`echo $output` ar trebui să menționeze `"$output"`, deoarece nu aveți nevoie în mod specific ca acesta să fie împărțit în cuvinte. Probabil că este în regulă, dar chiar și pe sistemele normale, unele medii amovibile ajung să fie montate pe căi cu spații în numele lor, ceea ce ar putea duce la mesaje de eroare din acest lucru în practică. Deci, regula generală, citați întotdeauna expansiunile variabile, cu excepția cazului în care doriți în mod special împărțirea cuvintelor sau un alt efect care blochează citatele.
drapel ua
Similar, dar folosind caracteristica `firstaction` a `logrotate`: https://serverfault.com/questions/372809/free-space-driven-log-rotation-on-linux
Eric Duminil avatar
drapel us
@NickMatteo Dacă codul corespunzător funcționează bine și este ușor de citit, este o prostie să argumentezi că este prostesc, deoarece ar putea fi scris diferit cu un limbaj de 45 de ani și nu foarte lizibil. Conductele UNIX sunt minunate și lizibile, cui îi pasă dacă comanda ar putea fi scrisă cu unul sau două conducte mai puțin?
drapel ao
@EricDuminil: Utilizarea oarecum comună a `grep 'PATTERN' | awk „{do stuff to matching lines}” este în mod obiectiv o prostie, deoarece întregul scop al awk este de a face chestii cu linii de potrivire și ai fi putut scrie doar „awk”/PATTERN/{do stuff}’”.
Eric Duminil avatar
drapel us
@NickMatteo: Ești liber să folosești orice instrumente cu care ești familiarizat. matigo pare să fie fericit cu `grep ... | awk ...`, ați fi mulțumit cu `awk '/PATTERN/ {do stuff}`, iar eu aș fi mulțumit cu un script Ruby sau Python scurt. Atâta timp cât scripturile funcționează și pot fi citite, nimeni nu greșește și nicio comandă nu este o prostie și, cu siguranță, nu este o prostie obiectivă.
drapel cn
@EricDuminil S-ar putea să nu fie proști în mod obiectiv, dar există un argument că un site de întrebări și răspunsuri ar trebui să învețe oamenii cum funcționează comenzile. Adăugarea `awk` face să pară că comanda nu o poate gestiona singură.Există, de asemenea, o valoare în a nu folosi mai multe comenzi prin pipe și a încetini procesul. Pe scurt, există criterii pentru a susține că folosirea „awk” așa cum este destinat să fie folosit este cel mai bun răspuns.
drapel bv
@matgio - Nu am putut face ca `oldfile=$(ls -dltr $DIR/*.mp4|awk '{ print $9 }' | head -1)` să funcționeze, dar am făcut ca acest lucru să funcționeze `oldfile=$( găsiți $DIR -nume "*.mp4" -tip f | sortare | head -n 1)` Dacă există vreun motiv pentru care metoda mea nu ar trebui folosită?
drapel in
@Beno â Primiți o eroare la prima comandă? În orice caz, dacă „găsește” vă va oferi ceea ce aveți nevoie, nu ezitați să îl utilizați. Scenariul este doar un „kit de pornire” pentru a vă pune în practică unele dintre lucrurile preliminare
Puncte:12
drapel kz

Aș folosi Python pentru o astfel de sarcină. Ar putea duce la mai mult cod decât o soluție bash pură, dar:

  • este (IMO) mai ușor de testat, doar utilizați pytest sau unitest modul
  • este lizibil pentru persoanele care nu sunt Linux (cu excepția get_device funcție care este specifică Linux...)
  • este mai ușor să începeți (din nou IMO)
  • Ce se întâmplă dacă vrei să trimiți niște e-mailuri? Pentru a declanșa noi acțiuni? Scripturile pot fi îmbogățite cu ușurință cu un limbaj de programare precum Python.

De la Python 3.3, shutil modulul vine cu o funcție numită utilizare_disc. Poate fi folosit pentru a obține utilizarea discului pe baza unui director dat.

Problema minoră este că nu știu cum să obțin cu ușurință numele discului, I.E, /dev/sdb, chiar dacă este posibil să obțineți utilizarea discului (folosind orice director montat pe /dev/sdb, În cazul meu $HOME de exemplu). Am scris o funcție numită get_device în acest scop.

#!/usr/bin/env python3
import argparse
din os.path import getmtime
din shutil import disk_usage, rmtree
de la ieșire de import sys
din calea de import pathlib
de la tastarea import Iterator, Tuple


def get_device(cale: Cale) -> str:
    """Găsiți montarea pentru un director dat. Acest lucru este necesar doar pentru logare."""
    # Citiți /etc/mtab pentru a afla despre punctele de montare
    mtab_entries = Path("/etc/mtab").read_text().splitlines()
    # Creați un dict de puncte de montare și dispozitive
    mount_points = dict([listă(inversat(line.split(" ")[:2])) pentru linia din mtab_entries])
    # Găsiți punctul de montare al căii date
    în timp ce path.resolve(True).as_posix() nu este în mount_points:
        cale = cale.părinte
    # Dispozitivul de returnare asociat cu punctul de montare
    returnează punctele_montare[path.as_posix()]


def get_directory_and_device(cale: str) -> Tuple[str, Path]:
    """Ieșiți din proces dacă directorul nu există."""
    fs_path = Cale(cale)
    # Calea trebuie să existe
    dacă nu fs_path.exists():
        print(f"EROARE: Nu există un astfel de director: {cale}")
        ieșire (1)
    # Și calea trebuie să fie un director valid
    dacă nu fs_path.is_dir():
        print(f"Calea trebuie să fie un director și nu un fișier: {cale}")
        ieșire (1)
    # Obțineți dispozitivul
    dispozitiv = get_device(fs_path)

    dispozitiv de returnare, fs_path


def get_disk_usage(cale: cale) -> float:
    # Shuil.disk_usage suport Obiecte asemănătoare căii, deci nu este nevoie să aruncați în șir
    utilizare = disk_usage(cale)
    # Obțineți utilizarea discului în procente
    return usage.used / usage.total * 100


def remove_file_or_directory(cale: Cale) -> Niciunul:
    """Eliminați calea dată, care poate fi un director sau un fișier."""
    # Eliminați fișierele
    dacă calea.is_file():
        path.unlink()
    # Ștergeți recursiv arbori de directoare
    dacă calea.is_dir():
        rmtree(cale)


def găsiți cele mai vechi_fișiere(
    cale: cale, model: str = "*", prag: int = 80
) -> Iterator[Cale]:
    """Repetați fișierele sau directoarele prezente într-un director care se potrivesc cu modelul dat."""
    # Listați fișierele din directorul primit ca argument și sortați-le după vârstă
    fișiere = sortate(cale.glob(model), cheie=getmtime)
    # Randamentul căilor fișierelor până când utilizarea este mai mică decât pragul
    pentru fișier în fișiere:
        utilizare = get_disk_usage(cale)
        dacă utilizare < prag:
            pauză
        dosar de randament


def check_and_clean(
    cale: str,
    prag: int = 80,
    eliminați: bool = False,
) -> Niciuna:
    """Functie principala"""
    dispozitiv, fspath = get_directory_and_device(cale)
    # Shuil.disk_usage suport Obiecte asemănătoare căii, deci nu este nevoie să aruncați în șir
    utilizare = disk_usage(cale)
    # Luați măsuri dacă este necesar
    dacă utilizare > prag:
        imprimare(
            f„Utilizarea discului este mai mare decât pragul: {usage:.2f}% > {threshold}% ({dispozitiv})”
        )
    # Iterați peste fișiere pentru a le elimina
    pentru fișierul din find_oldest_files(fspath, "*", threshold):
        print(f"Se elimină fișierul {fișier}")
        dacă eliminați:
            remove_file_or_directory(fișier)


def main() -> Nici unul:

    parser = argparse.ArgumentParser(
        description="Șterge fișierele vechi când utilizarea discului este peste limita."
    )

    parser.add_argument(
        "path", help="Cale directorului unde fișierele ar trebui să fie curățate", tip=str
    )
    parser.add_argument(
        "--prag",
        "-t",
        metavar="T",
        help="Pragul de utilizare în procent",
        tip=int,
        implicit=80,
    )
    parser.add_argument(
        "--elimina",
        "--rm",
        help="Fișierele nu sunt eliminate decât dacă este specificată opțiunea --removed sau --rm",
        action="store_true",
        implicit=fals,
    )

    args = parser.parse_args()

    verifica_si_curata(
        args.path,
        prag=args.threshold,
        remove=args.remove,
    )


if __name__ == "__main__":
    principal()

Dacă aveți nevoie să orchestrați multe sarcini folosind CRON, ar putea merita să puneți împreună un cod Python ca bibliotecă și să reutilizați acest cod în multe sarcini.

EDIT: Am adăugat în sfârșit partea CLI în script, cred că o voi folosi și eu

qwr avatar
drapel kr
qwr
fww puteți trimite e-mailuri din linia de comandă.
gcharbon avatar
drapel kz
Nu spun că nu se poate face în CLI, spun că OP nu este familiarizat cu bash și că i-ar putea fi mai ușor să o facă în Python
drapel bv
Mulțumesc pentru postarea cuprinzătoare! Îmi place o abordare mai lizibilă, dar, din păcate, nici nu sunt atât de familiarizat cu phython. Dar m-ai făcut să realizez că aș putea face asta cu php, cu care mă simt mult mai confortabil.
Clumsy cat avatar
drapel cn
+1 pentru ceva mai ușor de testat. Aș dori teste unitare pentru un script ca acesta.
Puncte:1
drapel tj

Verifica daca /dev/sbd/ a ajuns la 90% capacitate. Dacă da, ștergeți cel mai vechi fișier din (și fișierele din subdosare) /home/ben/ftp/surveillance/ Și repetă asta până când /dev/sbd/ capacitatea este sub 80% Repetați la fiecare 10 minute.

Scriptul de mai jos va face exact asta (cu condiția să îl adăugați la dvs crontab să ruleze la intervale de 10 minute). Asigurați-vă că acesta este ceea ce doriți cu adevărat să faceți, deoarece acest lucru s-ar putea șterge cu ușurință toate fișiere în /home/ben/ftp/surveillance/ dacă discul tău se umple undeva în afara acestui director.

#!/bin/sh
directory='/home/ben/ftp/surveillance'
max_usage=90
goal_usage=80
[ -d „$director” ] || iesirea 1
[ "$max_usage" -gt "$goal_usage" ] || iesirea 1
[ "$( df --output=pcent $director | \
    grep -Ewo '[0-9]+' )" -ge "$max_usage" ] || ieșire 0
dev_used="$( df -B 1K --output=utilizat $director | \
    grep -Ewo '[0-9]+' )"
goal_usage="$( printf "%.0f" \
    $( echo ".01 * $goal_usage * \
    $( df -B 1K --output=mărime $director | \
        grep -Ewo '[0-9]+' )" | bc ) )"
echo „$( find $directory -type f -printf '%Ts,%k,\047%p\047\n' )" | \
    sortare -k1 | \
        awk -F, -v goal="$(($dev_used-$goal_usage))" '\
            (sum+$2)>obiectiv{printf "%s ",$3; Ieșire} \
            (suma+$2)<=goal{printf "%s ",$3}; {sum+=$2}' | \
                xargs rm

Cum funcționează acest script:

Primele 3 rânduri după shebang sunt variabilele conform parametrilor dvs.:

  • director este calea completă către directorul părinte care conține fișierele și subdirectoarele din care doriți să eliminați fișierele vechi (de exemplu, /home/ben/ftp/surveillance). Ghilimelele din jurul acestei valori nu sunt necesare decât dacă calea conține spații.
  • max_usage este procentul din capacitatea discului care va declanșa acțiunile vechi de ștergere a fișierelor (de exemplu, 90 la sută).
  • scop_utilizare este procentul din capacitatea discului pe care doriți să-l obțineți după ștergerea fișierelor vechi (adică, 80 la sută).

Rețineți că valorile lui max_usage și scop_utilizare trebuie sa fie numere întregi.

[ -d „$director” ] || iesirea 1
  • Verifică asta director există, altfel scriptul se termină și iese cu starea 1.
[ "$max_usage" -gt "$goal_usage" ] || iesirea 1
  • Verifică asta utilizare_max este mai mare decât scop_utilizare, altfel scriptul se termină și iese cu starea 1.
[ "$( df --output=pcent $director | \
    grep -Ewo '[0-9]+' )" -ge "$max_usage" ] || ieșire 0
  • Obține procentul curent de capacitate a discului utilizat și verifică dacă îndeplinește sau depășește pragul stabilit de utilizare_max. Dacă nu, procesarea ulterioară nu este necesară, astfel încât scriptul se încheie și iese cu starea 0.
dev_used="$( df -B 1K --output=utilizat $director | \
    grep -Ewo '[0-9]+' )"
  • Obține capacitatea actuală a discului kiloocteți utilizați.
goal_usage="$( printf "%.0f" \
    $( echo ".01 * $goal_usage * \
    $( df -B 1K --output=mărime $director | \
        grep -Ewo '[0-9]+' )" | bc ) )"
  • Convertește scop_utilizare variabilă în kiloocteți (vom avea nevoie de această valoare mai jos).
găsiți $directory -type f -printf '%Ts,%k,\047%p\047\n'
  • Localizează toate fișierele în director (și în toate subdirectoarele sale) și face o listă cu aceste fișiere, câte unul pe linie, formatate ca marca temporală, dimensiune în kiloocteți, „full/path/to/file”. Rețineți că „complet/cale/spre/fișier” este cuprins între ghilimele simple, astfel încât spațiile din numele fișierelor sau directoarelor nu vor cauza probleme mai târziu.
sortează -k1
  • Sortează anterior ecouLista fișierelor după marcaj de timp (mai vechi mai întâi).
awk -F, -v goal="$(($dev_used-$goal_usage))"
  • awk creează o variabilă internă poartă care este egal cu diferența dintre dev_used și scop_utilizare - și aceasta este valoarea totală în kiloocteți a fișierelor care trebuie eliminate pentru a reduce procentul de capacitate a discului la scop_utilizare stabilit la începutul scenariului.
(sum+$2)>obiectiv{printf "%s ",$3; Ieșire} \
(suma+$2)<=goal{printf "%s ",$3}; {sum+=$2}'
  • awk (continuare) începe procesarea listei păstrând o sumă curentă a valorilor câmpului 2 (dimensiune în kiloocteți) și tipărirea valorilor câmpului 3 („complet/cale/spre/fișier”) la un șir separat de spațiu până când suma kiloocteților din câmpul 2 devine mai mare decât poartă, moment în care awk oprește procesarea liniilor suplimentare.
xargs rm
  • Șirul de valori „full/path/to/file” de la awk este canalizat la xargs care conduce rm comandă folosind șirul ca argumente. Acest lucru elimină acele fișiere.

Postează un răspuns

Majoritatea oamenilor nu înțeleg că a pune multe întrebări deblochează învățarea și îmbunătățește legătura interpersonală. În studiile lui Alison, de exemplu, deși oamenii își puteau aminti cu exactitate câte întrebări au fost puse în conversațiile lor, ei nu au intuit legătura dintre întrebări și apreciere. În patru studii, în care participanții au fost implicați în conversații ei înșiși sau au citit transcrieri ale conversațiilor altora, oamenii au avut tendința să nu realizeze că întrebarea ar influența – sau ar fi influențat – nivelul de prietenie dintre conversatori.