web-dev-qa-db-fra.com

Supprimer les fichiers les plus anciens de manière récursive jusqu'à atteindre la limite maximale

J'essaie de modifier script de Sergiy pour qu'il agisse de manière récursive (actuellement, il ne supprime qu'un fichier, mais je voudrais qu'il supprime tous les fichiers jusqu'à ce que la limite de fichier maximale soit atteinte). Mais je n'arrive pas à comprendre. Je pense que je devrais pouvoir modifier la première fonction ("delete old") pour lire plusieurs horodatages et la transmettre à la ligne suivante, mais je ne suis pas familier avec cette commande et je ne trouve aucune information à ce sujet. Toute idée serait appréciée!

3
MysticEagle

Si vous souhaitez que le script continue à fonctionner sur un répertoire, sans passer par les sous-répertoires, le comptage et la suppression peuvent être effectués dans une boucle while. La dernière section de la fonction main devrait être modifiée pour ressembler à ceci:

local file_inodes=$(get_files)
    while filecount_above_limit 
    do
        printf  "@@@ File count in %s is above %d." "$directory" $max_files
        printf "Will delete oldest\n"
        sort -k1 -n <<< "$file_inodes" | delete_oldest
        local file_inodes=$(get_files)
    done
    printf "@@@ File count in %s is below %d."  "$directory" $max_files
    printf "Exiting normally"

Attention!
Le problème de cette modification simple est que, si vous n’avez pas fait de commentaire dans la ligne de suppression située en haut, le script se mettra en boucle sans fin, car il recalculera le nombre de fichiers après chaque suppression. Si un fichier n'est pas supprimé, le nombre de fichiers reste le même et la boucle ne se ferme jamais.

Il est possible de modifier le script de manière plus complexe, de supprimer l'inode du fichier du tableau file_inodes une fois supprimé et d'incrémenter négativement la variable file_count plutôt que de répéter la ligne local file_inodes=$(get_files). Cela réglerait le problème de non-suppression, mais je le laisserai à quelqu'un d'autre.

4
Arronical

Je suggérerais une autre solution, qui ira de manière récursive dans l’arborescence des répertoires de destination et supprimera tous les fichiers, à l’exception d’un certain nombre prédéfini de nouveaux fichiers. Cette solution est basée sur: (1) script bash récursif et (2) explication d'un script shell pour imprimer récursivement une arborescence de répertoires complète .

1. Créez un fichier script exécutable, appelé walkr (, marchez et supprimez ), qui se trouve dans _/usr/local/bin_ pour être accessible en tant que commande Shell ( étapes plus détaillées ).

2. Le contenu du script walkr est assez simple:

_#!/bin/bash

[[ -z "${NFK}" ]] && NFK='7' || NFK="$NFK"
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "${1}" && ABS_PATH="${PWD}"

file_operations() {
        local IFS=$'\t\n' # Change the value of the Internal Field Separator locally
        rm $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null
}

walk() {
        cd "$1" && file_operations # Change directory to the destination path and call the above function
        for item in "$1"/*; do [[ -d "$item" ]] && walk "$item"; done # Make the recursion
}

walk "${ABS_PATH}"
_

3. Explication:

  • Au début, le script vérifiera si la variable _$NFK_ (qui détermine le nombre de fichiers à conserver) est définie à l’avance - la condition _[[ -z "${NFK}" ]]_. S'il n'est pas défini, la valeur par défaut est _7_.

  • Ensuite, le script traite du chemin de destination (stdin de la commande). S'il n'est pas fourni - la condition _[[ -z "${1}" ]]_ - le script fonctionnera dans le répertoire en cours.

  • Enfin, la fonction principale walk() sera exécutée.


  • La fonction walk():

    • Initialement, le répertoire sera remplacé par le chemin de destination _cd "$1"_, puis il appellera la fonction file_operations(), qui fonctionnera à l'intérieur.

    • En outre, pour chaque _$item_, dans le répertoire en cours _"$1"/*_, qui est également le répertoire _[[ -d "$item" ]]_, la fonction walk() sera exécutée à nouveau, ainsi nous créons la récursion .

  • La fonction file_operations():

    • Initialement, il va définir une valeur locale de variable Bash interne _$IFS_, donc nous pouvons gérer correctement _<the list of the files to be removed>_, peu importe qu'il y ait des espaces à l'intérieur du noms de fichiers séparés.

    • De plus, la commande rm $(<the list of the files to be removed>) sera exécutée. La redirection des erreurs _2>/dev/null_ est pour ces cas où il n'y a rien à supprimer. _<the list of the files to be removed>_ est pris de cette façon:

      • La commande _ls -lt_ répertorie le contenu du répertoire en cours au format de liste longue _-l_ et la liste est triée par heure de modification, la plus récente en premier _-t_. Et cette liste est transmise _|_ à la commande suivante.

      • La commande suivante _grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*'_ rognera ces lignes commençant par _^_ par _-_, depuis leur début jusqu’au motif _[0-9]{2}:[0-9]{2}__. L'option _-P_ avec l'option _-o_ générera les chaînes correspondant au modèle _^-.*[0-9]{2}:[0-9]{2}__. La notification _\K_ ignorera la partie correspondante avant elle-même. ( source - cette réponse utile )

        Ainsi, nous n'obtiendrons que les noms des fichiers de la liste. Dans la sortie de _ls -l_, les lignes décrivant les répertoires commencent par d, et celles des fichiers commencent par _-_. ( source de l'idée )

        Ce modèle correspond au format d'heure _00:00_.

      • Enfin, la commande tail -n +"$((NFK+1)) coupera les premières lignes de notre liste de fichiers. Le nombre de ces premières lignes est égal à la valeur de _$NFK_ plus 1, il s'agit d'une exigence de la commande tail.

4. Exemples d'utilisation:

  • Pour exécuter walkr pour le répertoire actuel:

    _walkr      # You shouldn't use any argument, 
    walkr ./   # but you can use also this format
    _
  • Pour exécuter walkr pour n’importe quel répertoire enfant:

    _walkr <directory name>
    walkr ./<directory name>
    walkr <directory name>/<sub directory>
    _
  • Pour exécuter walkr pour tout autre répertoire:

    _walkr /full/path/to/<directory name>
    _
  • Pour changer le nombre de fichiers à conserver (à _3_ par exemple), utilisez ce format

    _NFK=3 walkr
    NFK=3 walkr /full/path/to/<directory name>
    # etc.
    _

5. Jouons avec le script walkr:

  • Nous pouvons utiliser la commande touch file.name -d "1 hour ago" pour créer un fichier vide daté d’une heure auparavant. Nous pouvons donc utiliser les commandes suivantes pour créer une structure de répertoire telle que présentée ici .

    _mkdir -p ~/temp/dir{A..C} && cd ~/temp ;\
    DEST='';      touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirA/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirB/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirC/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago"
    _
  • Nous pouvons maintenant effectuer quelques tests:

    enter image description here


Mise à jour de la fonctionnalité du script. Voici la version mise à jour du script ci-dessus:

_#!/bin/bash

[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "$1" && ABS_PATH="${PWD}"
[[ -z "${2}" ]] && NFK='7' || NFK="$2"  # Number of the files to be kept
[[ -z "${3}" ]] && REC='1' || REC="$3"  # REC='1' - work recursively
[[ -z "${4}" ]] && VRB='1' || VRB="$4"  # VRB='1' - work in verbose mode

file_operations() {
        local IFS=$'\t\n' # Change the value of the Internal Field Separator locally

        if [ "$VRB" == "1" ]
        then # Verbose mode:
                rm -v $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null && printf " -from: '%s' \n" "$1" || echo "nothing to remove in: '$1'"
        else # Quiet mode:
                rm $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null
        fi
}

walk() {
        # Change directory to the destination path and call the above function, pass $1 for the verbose mode
        cd "$1" && file_operations "$1"
        # If REC='1': Recursive mode -- Make the recursion; otherwise work on the curent level
        if [ "$REC" == "1" ]; then for item in "$1"/*; do [[ -d "$item" ]] && walk "$item"; done; fi
}

walk "${ABS_PATH}"
_
  • Cette version du script peut gérer quelques variables d'entrée supplémentaires. Il a les modes silencieux et verbeux et peut fonctionner de manière non récursive.

  • Le format complet est:

    _walkr '<destination path>' '<number of lines to be kept>' '<no recursion>' '<quiet>'
    _

    Où le contenu exact de _<no recursion>_ et _<quiet>_ est sans importance. Seules les variables d'entrée _$3_ et _$4_ ne doivent pas être vides pour que le comportement par défaut soit écrasé.

2
pa4080