Python >> Tutoriel Python >  >> Python

Python shutdown :opérations de fichiers de haut niveau démystifiées

Vous cherchez à copier, déplacer, supprimer ou archiver des données avec vos programmes Python ? Si oui, vous êtes au bon endroit car cet article est entièrement consacré au module spécialement conçu pour le travail. Il s'appelle shutdown (abréviation d'utilitaires shell) et nous démystifierons ses principales fonctionnalités à l'aide de quelques exemples simples. Nous verrons également comment utiliser shutdown en combinaison avec d'autres modules de bibliothèque standard, et couvrirons quelques limitations qui pourraient vous causer un peu de mal de tête en fonction de vos priorités, du système d'exploitation que vous utilisez et de votre version de Python.

Un mot sur les chemins de fichiers

Avant de commencer, il convient de mentionner que les chemins sont construits différemment selon votre système d'exploitation. Sur Mac et Linux, ils sont séparés par des barres obliques (connues sous le nom de style Posix) et sur Windows par des barres obliques inverses.

Pour les besoins de cet article, j'utiliserai des chemins de style Windows pour illustrer les fonctionnalités de shutdown, mais cela aurait tout aussi bien pu être fait avec des chemins Posix.

Le fait que les chemins Windows utilisent des barres obliques inverses entraîne également une autre complication car ils ont une signification particulière en Python. Ils sont utilisés dans le cadre de caractères spéciaux et à des fins d'échappement, que vous pouvez lire dans cet article sur la barre oblique inversée de Finxter.

Vous remarquerez donc la lettre "r" avant les chaînes dans les extraits de code - ce préfixe signifie une chaîne brute dans laquelle les barres obliques inverses sont traitées comme des caractères littéraux plutôt que comme des caractères spéciaux. L'autre façon de gérer ce problème consiste à utiliser une seconde barre oblique inverse pour échapper à la première, qui est le format utilisé par Python pour afficher le chemin Windows d'un nouveau fichier qui a été créé.

En passant, lorsque vous utilisez des chemins dans vos programmes réels, je vous recommande fortement de les définir avec pathlib.Path(). Si cela est fait correctement, cela a pour effet de normaliser les chemins afin qu'ils fonctionnent quel que soit le système d'exploitation sur lequel le programme s'exécute.

Shutil Directory et opérations sur les fichiers

shutil copie

Alors, commençons par un exemple simple de la façon de copier un seul fichier d'un dossier à un autre.

Il n'est pas nécessaire d'installer quoi que ce soit par pip car shutdown est dans la bibliothèque standard de Python; importez simplement le module et vous êtes prêt à partir :

 >>> import shutil
 >>> source = r'C:\src_folder\blueprint.jpg'
 >>> destination = r'C:\dst_folder'
 >>> shutil.copy(source, destination)
 
 'C:\\dst_folder\\blueprint.jpg'

shutil.copy() place une copie du fichier source spécifié dans le dossier de destination que vous avez défini, et Python confirme le chemin d'accès au fichier. Les autorisations du fichier sont copiées avec les données. Une autre option consiste à spécifier un fichier de destination au lieu d'un dossier de destination :

 ...
 >>> source = r'C:\src_folder\blueprint.jpg'
 >>> destination = r'C:\dst_folder\plan.jpg'
 >>> shutil.copy(source, destination)
 
 'C:\\dst_folder\\plan.jpg'

Dans ce cas, une copie du fichier source sera toujours placée dans le dossier de destination mais son nom sera remplacé par celui qui a été fourni.

AVERTISSEMENT : Que vous copiez un fichier directement dans un dossier en conservant son nom existant ou que vous fournissiez un nom de fichier de destination, si un fichier existe déjà dans le dossier de destination avec ce nom copy() va l'écraser définitivement sans vous prévenir au préalable .

Cela peut être utile si vous cherchez intentionnellement à mettre à jour ou à remplacer un fichier, mais cela peut causer des problèmes majeurs si vous oubliez qu'il y a un autre fichier à l'emplacement portant ce nom que vous souhaitez conserver !

shutil copie2

copy2() fonctionne de la même manière que copy() sauf qu'en plus des autorisations de fichier, il tente également de préserver les métadonnées telles que la dernière fois que le fichier a été modifié.

Il y a quelques limitations à cela, que vous pouvez lire dans les Métadonnées de fichiers manquantes section plus loin dans cet article.

shutil copytree

Si copier des fichiers un par un ne suffit pas, copytree() est la voie à suivre.

 ...
 >>> source = r'C:\src_folder\directory'
 >>> destination = r'C:\dst_folder\directory_copy'
 >>> shutil.copytree(source, destination)
 
 'C:\\dst_folder\\directory_copy'

copytree() crée une copie du répertoire source entier et lui donne le nom que vous spécifiez dans le chemin de destination. Il utilise copy2() pour copier les fichiers par défaut, il tentera donc de préserver les métadonnées, mais cela peut être remplacé en définissant le paramètre copy_function. Contrairement à la copie de fichiers individuels, si un répertoire portant le même nom existe déjà dans cette destination (dans ce cas, directory_copy ), une erreur sera levée et l'arborescence des répertoires ne sera pas copiée. Ainsi, lorsque vous tentez de terminer la même opération de copytree pour la deuxième fois, voici une version abrégée de ce que nous voyons :

 ...
 FileExistsError: [WinError 183] Cannot create a file when that file already  
 exists: 'C:\\dst_folder\\directory_copy'

Écraser accidentellement un répertoire entier pourrait être assez catastrophique, et cette sauvegarde a sans aucun doute empêché de nombreux incidents de ce type au fil des ans. Cela a également causé beaucoup de frustration, car jusqu'à très récemment, il n'y avait pas de moyen simple de le remplacer.

Si remplacer un répertoire existant EST ce que vous voulez faire, une nouvelle option a été introduite dans Python 3.8 qui rend cela possible :

 ...
 >>> shutil.copytree(source, destination, dirs_exist_ok=True)
 
 'C:\\dst_folder\\directory_copy'

Le dirs_exist_ok Le paramètre est défini sur False par défaut, mais le changer sur True annule le comportement habituel et nous permet de compléter notre copytree() opération une deuxième fois même si directory_copy existe déjà à l'emplacement spécifié. Une autre fonctionnalité pratique est le paramètre ignore :

 from shutil import copytree, ignore_patterns
 
 >>> src = r'C:\src_folder\another_directory'
 >>> dst = r'C:\dst_folder\another_directory_copy'
 >>> shutil.copytree(src, dst, ignore=ignore_patterns('*.txt', 'discard*'))
 
 'C:\\dst_folder\\another_directory_copy'

ignore vous permet de spécifier les fichiers et les dossiers à omettre lorsqu'un répertoire est copié.

Le moyen le plus simple d'y parvenir est d'importer le ignore_patterns de shutdownil fonction d'assistance, qui peut ensuite être transmise au paramètre ignore de copytree.

ignore_patterns prend un ou plusieurs modèles au format chaîne, et tous les fichiers ou dossiers correspondants seront ignorés lorsque copytree() crée la nouvelle version du répertoire.

Par exemple, dans l'extrait de code ci-dessus, nous avons passé deux arguments à ignore_patterns :'*.txt' et 'discard*' . L'astérisque (symbole *) agit comme un caractère générique qui correspond à zéro ou plusieurs caractères, donc ces modèles garantiront que copytree() duplique tout sauf les fichiers qui se terminent par .txt et les fichiers ou dossiers qui commencent par abandonner. Cela peut être vu en affichant la structure de fichier de another_directory :

 C:\src_folder>tree /F
 ...
 C:.
 └───another_directory
     ├───discard_this_folder
     ├───include_this_folder
     │       discard_this_file.docx
     │       include_this_file.docx
     │       include_this_file_too.docx
     │       this_file_will_be_discarded.txt
     │       this_file_will_not_be_discarded.pdf
     │
     └───include_this_folder_too

Et puis en regardant la structure du fichier d'un autre_répertoire_copie une fois qu'il a été créé par shutdown :

C:\dst_folder>tree /F
 ...
 C:.
 └───another_directory_copy
     ├───include_this_folder
     │       include_this_file.docx
     │       include_this_file_too.docx
     │       this_file_will_not_be_discarded.pdf
     │
     └───include_this_folder_too

shutil move

move() fonctionne de la même manière que copy2() mais vous permet de transférer un fichier vers un autre emplacement au lieu de le copier.

Vous pouvez également déplacer un répertoire entier en spécifiant un dossier dans lequel le placer :

 import shutil
 
 
 >>> source = r'C:\src_folder\diagrams'
 >>> destination = r'C:\dst_folder'
 >>> shutil.move(source, destination)
 
 'C:\\dst_folder\\diagrams'

Vous pouvez également fournir un nouveau nom pour le répertoire dans le cadre du processus :

 ...
 >>> source = r'C:\src_folder\diagrams'
 >>> destination = r'C:\dst_folder\layouts'
 >>> shutil.move(source, destination)
 
 'C:\\dst_folder\\layouts'

Contrairement à copy() et copy2() , move() lèvera une exception si un fichier portant le même nom existe déjà dans le dossier donné (sauf s'il ne se trouve pas sur le système de fichiers actuel). Ce comportement peut également être observé lors du déplacement de répertoires. Après avoir déplacé notre répertoire de diagrammes et l'avoir renommé layouts, si nous essayons maintenant de déplacer un autre répertoire appelé layouts au même emplacement, nous verrons ce qui suit :

...
 >>> source = r'C:\src_folder\layouts'
 >>> destination = r'C:\dst_folder'
 >>> shutil.move(source, destination) 
 ...
 shutil.Error: Destination path 'C:\dst_folder\layouts' already exists
 

AVERTISSEMENT :Cependant, comme pour les fonctions de copie, lors du déplacement de fichiers individuels, si vous incluez un nom de fichier de destination et qu'un fichier portant ce nom existe déjà dans le dossier de destination, move() va l'écraser définitivement sans vous en avertir :

...
 >>> source = r'C:\src_folder\sketch.jpg'
 >>> destination = r'C:\dst_folder\design.jpg'
 >>> shutil.move(source, destination)
 
 'C:\\dst_folder\\design.jpg'
 
 >>> source = r'C:\src_folder\different_sketch.jpg'
 >>> destination = r'C:\dst_folder\design.jpg'
 >>> shutil.move(source, destination)
 
 'C:\\dst_folder\\design.jpg'


Il y a un autre piège subtil à surveiller lors de l'utilisation de move() qui peut également causer des problèmes :

...
 >>> source = r'C:\src_folder\blueprint.jpg'
 >>> destination = r'C:\dst_folder\plan'
 >>> shutil.move(source, destination)
 
 'C:\\dst_folder\\plan'

A cette occasion, nous avons essayé de transférer un fichier dans un dossier qui n'existe pas. Au lieu de déclencher une exception, move() a terminé l'opération et donné au fichier le nom du répertoire inexistant (plan) sans extension de fichier . Le fichier est toujours au format JPEG, mais il ne s'appellera pas comme on s'y attend, et le système de fichiers ne le reconnaîtra plus !

Le même type de problème pourrait survenir si nous manquions accidentellement l'extension de fichier d'un nom de fichier de destination.

Ce problème peut également survenir lors de l'utilisation des fonctions de copie si vous ne faites pas attention. Dans ce cas, vous auriez au moins le fichier original pour référence, mais cela pourrait encore entraîner une confusion importante.

shutil rmtree

Si vous souhaitez supprimer un répertoire entier au lieu de le déplacer ou de le copier, vous pouvez le faire avec rmtree() :

 
 import shutil
 >>> shutil.rmtree(r'C:\dst_folder\directory_copy')

Par défaut, rmtree() déclenchera une exception et arrêtera le processus si une erreur est rencontrée lors de la tentative de suppression de fichiers. Vous pouvez voir un exemple de l'un de ces messages d'erreur ci-dessous :

 ...
 PermissionError: [WinError 32] The process cannot access the file because 
 it is being used by another process: 
 'C:\\dst_folder\\directory_copy\\blueprint.pdf'


Cependant, ce comportement peut être remplacé :

 ...
 >>> shutil.rmtree(r'C:\dst_folder\directory_copy', ignore_errors=True)


Si vous définissez le paramètre ignore_errors sur True, rmtree() continuera à supprimer le répertoire au lieu de déclencher une exception.

AVERTISSEMENT : Les arborescences de répertoires supprimées par rmtree() sont définitivement supprimées, vous devez donc faire très attention à la façon dont vous les utilisez. Si vous êtes préoccupé par les risques potentiels (et je ne vous blâmerais pas si vous l'étiez !), vous voudrez peut-être envisager d'utiliser une alternative plus sûre telle que Send2Trash.

archive Shutil

Vous pouvez également utiliser shutdown pour créer des archives de répertoire :

 ...
 >>> shutil.make_archive(
         r'C:\dst_folder\zipped_designs', 
         'zip', 
         r'C:\src_folder\designs',
         )
 
 'C:\\dst_folder\\zipped_designs.zip'


Comme indiqué ci-dessus, un moyen simple de le faire est de passer trois arguments à la fonction make_archive() :

  1. Le chemin où la nouvelle archive doit être créée, y compris son nom mais sans l'extension de fichier.
  2. Le format d'archive à utiliser lors de sa création.
  3. Le chemin du répertoire à archiver.

Le répertoire restera inchangé à son emplacement d'origine et l'archive sera créée à l'emplacement spécifié.

make_archive() peut également créer des archives aux formats .tar, .gztar, .bztar ou .xztar.

Pour les opérations plus sophistiquées que l'archivage d'un répertoire entier, comme la compression de fichiers sélectionnés à partir d'un répertoire basé sur des filtres, vous pouvez utiliser le module zipfile à la place.

Limitations de Shutil

Vous pouvez réaliser beaucoup de choses avec le module shutdown, mais, comme mentionné au début de cet article, il a quelques limitations que vous devez connaître.

Métadonnées de fichier manquantes

copy2() préserve autant de métadonnées que possible et est utilisé par copytree() et move() donc par défaut ces méthodes feront la même chose. Cependant, il n'est pas capable de tout capturer.

Sous Windows :les propriétaires de fichiers, les listes de contrôle d'accès (ACL) et les flux de données alternatifs ne sont pas copiés.

Les propriétaires de fichiers et les ACL sont également perdus sous Linux et Mac, ainsi que les groupes.

Sur Mac OS, la fourchette de ressources et les autres métadonnées ne sont pas non plus utilisées, ce qui entraîne la perte de données de ressources et des codes de créateur et de type de fichier incorrects.

Vitesse

Une plainte souvent adressée à shutdown dans le passé était qu'il pouvait être très lent à utiliser lorsque vous travaillez avec de grandes quantités de données, en particulier sous Windows.

Heureusement, ce problème a été résolu dans Python 3.8 avec l'introduction des opérations de copie efficaces dépendantes de la plate-forme.

Cette amélioration de "copie rapide" signifie que les opérations de copie et de déplacement des shutdowns sont désormais optimisées pour se produire dans le noyau du système d'exploitation concerné au lieu des tampons de l'espace utilisateur de Python dans la mesure du possible.

Par conséquent, si vous rencontrez des problèmes de vitesse sur une version antérieure de Python et que l'utilisation de 3.8 à la place est une option, cela améliorera probablement considérablement les choses.

Vous pouvez également rechercher des packages tiers tels que pyfastcopy.

Combiner Shul avec d'autres modules de bibliothèque standard

Dans la section copytree() de cet article, nous avons vu comment exercer un meilleur contrôle sur le comportement de shutdown en utilisant le paramètre ignore pour exclure les fichiers avec un nom ou un type particulier.

Mais que se passe-t-il si vous souhaitez effectuer des tâches plus complexes telles que l'accès à d'autres données liées aux fichiers afin de pouvoir les vérifier pour déterminer quelles opérations doivent être effectuées ?

L'utilisation de shutdown en combinaison avec certains des autres modules de bibliothèque standard de Python est la réponse.

Cette section est destinée à fournir un exemple d'un cas d'utilisation pour ce type d'approche.

Nous allons créer un programme simple qui peut nettoyer un répertoire de fichiers en stockant les anciens sous-répertoires s'ils n'ont pas été modifiés depuis longtemps.

Pour ce faire, nous utiliserons shutdown.move() ainsi que plusieurs autres modules pratiques, notamment :pathlib (que j'ai mentionné au début), os et time.

Les modules

En plus de simplifier considérablement la définition de chemins compatibles entre plates-formes, la classe Path de pathlib contient des méthodes qui aident vraiment à gérer efficacement les chemins de fichiers.

Nous utiliserons également la fonction walk du module os, qui n'a pas d'équivalent dans pathlib. Cela nous permettra de parcourir nos sous-répertoires pour identifier tous les fichiers qu'ils contiennent et extraire leurs chemins.

Nous tirerons également parti du module de temps, afin de pouvoir calculer le temps écoulé depuis la dernière modification des fichiers de chaque sous-répertoire.

Préparer le déménagement

Après avoir importé nos modules :

 import os
 import pathlib
 import shutil
 import time


La première chose que nous devons faire est d'assigner le nombre normal de secondes dans une année à une constante :

SECONDS = 365 * 24 * 60 * 60


Cela nous aidera à déterminer depuis combien de temps les fichiers de nos sous-dossiers ont été modifiés pour la dernière fois (nous en reparlerons plus tard).

Ensuite, nous définissons notre première fonction qui préparera les opérations de fichiers nécessaires pour terminer le déménagement :

 ...
 def prepare_move(number, path, storage_folder):
     pass


Notre fonction prend trois arguments :

  1. nombre - le nombre d'années écoulées depuis la dernière modification d'un fichier dans un sous-dossier (il peut également s'agir d'un nombre flottant tel que 1,5).
  2. path - le chemin du fichier du répertoire principal qui contient les sous-répertoires que nous voulons ranger.
  3. storage_folder - le nom du dossier où nous voulons que les anciens répertoires soient placés. Une fois l'opération terminée, ce dossier de stockage sera placé dans le répertoire principal à côté des sous-répertoires qui n'ont pas été déplacés.

Nous devons maintenant affecter des objets à des variables qui joueront un rôle important dans le processus de préparation :

 ...
 def prepare_move(number, path, storage_folder):
     length = SECONDS * number
     now = time.time()
     my_directory = pathlib.Path(path)
     my_subdirectories = (item for item in my_directory.iterdir() if item.is_dir())
  1. longueur - est le résultat de la multiplication de la constante SECONDES que nous avons précédemment définie par le nombre d'années passées dans la fonction.
  2. maintenant – est l'heure actuelle en secondes fournie par le module de temps. Ceci est calculé en fonction de ce qu'on appelle l'époque.
  3. my_directory - stocke le chemin du répertoire principal que nous avons transmis à la fonction en tant qu'objet pathlib.Path.
  4. my_subdirectories - est un générateur contenant les chemins de nos sous-répertoires produits en itérant dans my_directory.

Notre prochaine étape consiste à créer une boucle for pour parcourir les sous-répertoires générés par notre générateur et ajouter les détails de ceux qui n'ont pas été modifiés pendant la période que nous avons spécifiée à une liste d'opérations sur les fichiers :

 ...
 def prepare_move(number, path, storage_folder):
     length = SECONDS * number
     now = time.time()
     my_directory = pathlib.Path(path)
     my_subdirectories = (item for item in my_directory.iterdir() if item.is_dir())
     file_operations = []
     for subdirectory in my_subdirectories:
         time_stats = _get_stats(subdirectory)


La première tâche effectuée par la boucle est de créer une liste de tous les fichiers modifiés fois dans un sous-répertoire.

Ceci est géré par une fonction distincte qui utilise la méthode os walk mentionnée précédemment et la dernière valeur modifiée en secondes (st_mtime) disponible via l'utilitaire Path.stat() :

 ...
 def _get_stats(subdirectory):
     time_stats = []
     for folder, _, files in os.walk(subdirectory):
         for file in files:
             file_path = pathlib.Path (folder) / file
             time_stat = file_path.stat().st_mtime
             time_stats.append(time_stat)
     return time_stats

La boucle vérifie ensuite ces statistiques modifiées du fichier pour voir si elles précèdent toutes le point spécifié dans le temps (le calcul étant effectué en secondes).

Si c'est le cas, les chemins source et destination nécessaires sont construits et ajoutés à la liste file_operations.

Une fois que la boucle a parcouru tous nos sous-répertoires, la fonction renvoie la liste des opérations sur les fichiers qui doivent être effectuées :

 ...
 def prepare_move(number, path, storage_folder):
     length = SECONDS * number
     now = time.time()
     my_directory = pathlib.Path(path)
     my_subdirectories = (item for item in my_directory.iterdir() if item.is_dir())
     file_operations = []
     for subdirectory in my_subdirectories:
         time_stats = _get_stats(subdirectory)
         if all(time_stat < (now - length) for time_stat in time_stats):
             *_, subdirectory_name = subdirectory.parts
             source = subdirectory
             destination = my_directory / storage_folder / subdirectory_name
             file_operations.append((source, destination))
     return file_operations


Déplacer les sous-répertoires

Nous devons maintenant définir la fonction qui déplacera réellement le fichier :

 ...
 def move_files(file_operations):
     for operation in file_operations:
         source, destination = operation
         shutil.move(source, destination)


Étant donné que tout le travail de préparation a déjà été effectué, cette fonction accepte simplement les opérations sur les fichiers et les transmet à shutdown.move() via une boucle for afin que chaque ancien sous-répertoire puisse être placé dans le dossier de stockage spécifié.

Exécuter le programme

Enfin, nous définissons un main() fonction pour exécuter le programme et l'appeler avec nos arguments :

 ...
 def main(number, path, storage_folder):
     file_operations = prepare_move(number, path, storage_folder)
     move_files(file_operations)
 
 main(1, r"F:\my_directory", "old_stuff")


Voici tout le programme :

 
 import os
 import pathlib
 import shutil
 import time
 
 
 SECONDS = 365 * 24 * 60 * 60
 
 
 def prepare_move(number, path, storage_folder):
     length = SECONDS * number
     now = time.time()
     my_directory = pathlib.Path(path)
     my_subdirectories = (item for item in my_directory.iterdir() if item.is_dir())
     file_operations = []
     for subdirectory in my_subdirectories:
         time_stats = _get_stats(subdirectory)
         if all(time_stat < (now - length) for time_stat in time_stats):
             *_, subdirectory_name = subdirectory.parts
             source = subdirectory
             destination = my_directory / storage_folder / subdirectory_name
             file_operations.append((source, destination))
     return file_operations
 
 
 def _get_stats(subdirectory):
     time_stats = []
     for folder, _, files in os.walk(subdirectory):
         for file in files:
             file_path = pathlib.Path (folder) / file
             time_stat = file_path.stat().st_mtime
             time_stats.append(time_stat)
     return time_stats
 
 
 def move_files(file_operations):
     for operation in file_operations:
         source, destination = operation
         shutil.move(source, destination)
 
 
 def main(number, path, storage_folder):
     file_operations = prepare_move(number, path, storage_folder)
     move_files(file_operations)
 
 main(1, r"F:\my_directory", "old_stuff")

Vous pouvez voir à quoi ressemblait la structure des répertoires avant d'exécuter le programme ci-dessous :

 F:\my_directory>tree /F
 ...
 F:.
 ├───new_files_1
 │   │   new_file.jpg
 │   │
 │   ├───second_level_folder_1
 │   │       really_new_file.txt
 │   │
 │   └───second_level_folder_2
 │           very_new_file.txt
 │
 ├───new_files_2
 │       fairly_new_file.txt
 │
 ├───old_files_1
 │   │   old_file.txt
 │   │
 │   └───second_level_folder_1
 │       │   old_file_as_well.txt
 │       │
 │       └───third_level_folder
 │               really_old_file.jpg
 │
 └───old_files_2
     │   another_old_file.txt
     │
     └───old_second_level_folder
             oldest_file.jpg
             old_file_2.txt

Et voici à quoi ça ressemble après :

 
 F:\my_directory>tree /F
 ...
 F:.
  ├───new_files_1
  │   │   new_file.jpg
  │   │
  │   ├───second_level_folder_1
  │   │       really_new_file.txt
  │   │
  │   └───second_level_folder_2
  │           very_new_file.txt
  │
  ├───new_files_2
  │       fairly_new_file.txt
  │
  └───old_stuff
      ├───old_files_1
      │   │   old_file.txt
      │   │
      │   └───second_level_folder_1
      │       │   old_file_as_well.txt
      │       │
      │       └───third_level_folder
      │               really_old_file.jpg
      │
      └───old_files_2
          │   another_old_file.txt
          │
          └───old_second_level_folder
                  oldest_file.jpg
                  old_file_2.txt 


De toute évidence, si vous aviez un répertoire aussi petit ou un répertoire dans lequel tous les sous-répertoires étaient déjà étiquetés comme anciens ou nouveaux, vous n'auriez probablement pas besoin d'un tel programme ! Mais j'espère que cet exemple de base aide à illustrer comment le processus fonctionnerait avec un répertoire plus grand et moins intuitif.

Le programme présenté dans cette section a été grandement simplifié à des fins de démonstration. Si vous souhaitez voir une version plus complète, structurée comme une application en ligne de commande qui résume les modifications avant de décider de les appliquer, et vous permet de ranger les fichiers en fonction de la création et des dernières heures d'accès, vous pouvez la voir ici.

Réflexions finales

Comme nous l'avons vu, le module shutdown fournit d'excellents utilitaires pour travailler avec des fichiers et des répertoires, et vous pouvez grandement améliorer leur puissance et leur précision en les combinant avec d'autres outils de la bibliothèque standard et au-delà.

Des précautions doivent être prises pour éviter d'écraser ou de supprimer définitivement les fichiers et répertoires existants par accident. Veuillez donc consulter les avertissements inclus dans les sections pertinentes de cet article si vous ne l'avez pas déjà fait.

L'exemple de programme décrit ci-dessus n'est qu'une des nombreuses utilisations auxquelles les outils de shutdown pourraient être destinés. Nous espérons que vous trouverez bientôt des moyens ingénieux de les appliquer dans vos propres projets.