7
votes

Comparez le contenu des fichiers CSV avec filecmp et ignorez les métadonnées

import filecmp

comparison = filecmp.dircmp(dir_local, dir_server)
comparison.report_full_closure()
I want to compare all CSV files kept on my local machine to files kept on a server. The folder structure is the same for both of them. I only want to do a data comparison and not metadata (like time of creation, etc). I am using filecmp but it seems to perform metadata comparison. Is there a way to do what I want?

1 commentaires

Avez-vous essayé difflib ?


9 Réponses :


1
votes

Selon la documentation de filecmp :

Le module filecmp définit des fonctions pour comparer des fichiers et des répertoires, avec divers compromis optionnels temps / exactitude. Pour comparer des fichiers, consultez également le module difflib.

Plus précisément, les fichiers .cmp et .cmpfiles comparent les fichiers à l'aide de leurs signatures, ainsi que d' autres métadonnées:

filecmp.cmp (f1, f2, peu profond = Vrai)

Comparez les fichiers nommés f1 et f2, en retournant True s'ils semblent égaux, False sinon. Si shallow est vrai, les fichiers avec des signatures os.stat () identiques sont considérés comme égaux. Sinon, le contenu des fichiers est comparé. Notez qu'aucun programme externe n'est appelé à partir de cette fonction, ce qui lui confère portabilité et efficacité. Cette fonction utilise un cache pour les comparaisons passées et les résultats, avec des entrées de cache invalides si les informations os.stat () pour le fichier changent. Le cache entier peut être effacé en utilisant clear_cache ().

filecmp.cmpfiles (dir1, dir2, commun, peu profond = Vrai)

Comparez les fichiers des deux répertoires dir1 et dir2 dont les noms sont donnés par common. Renvoie trois listes de noms de fichiers: correspondance, non-correspondance, erreurs. match contient la liste des fichiers qui correspondent, mismatch contient les noms de ceux qui ne correspondent pas, et errors répertorie les noms des fichiers qui n'ont pas pu être comparés. Les fichiers sont répertoriés dans les erreurs s'ils n'existent pas dans l'un des répertoires, si l'utilisateur n'a pas l'autorisation de les lire ou si la comparaison n'a pas pu être effectuée pour une autre raison. Le paramètre shallow a la même signification et la même valeur par défaut que pour filecmp.cmp (). Par exemple, cmpfiles ('a', 'b', ['c', 'd / e']) comparera a / c avec b / c et a / d / e avec b / d / e. «c» et «d / e» seront chacun dans l'une des trois listes renvoyées.

De plus, si vous souhaitez une sortie diff, pensez à utiliser difflib indiqué dans la documentation de filecmp .

Références

filecmp: https://docs.python.org/3/library/filecmp.html

difflib: https://docs.python.org/3/library/difflib.html#module-difflib


0 commentaires

1
votes

Essayez d'utiliser git et diff.

https://gitpython.readthedocs.io/en/stable/

from git import Repo

repo = Repo('my_repo')

# Check differences between current files and last commit
diff = repo.git.diff(repo.head.commit.tree)
print(diff)


0 commentaires

1
votes

Si vous avez besoin d'une solution ponctuelle, winmerge compare des fichiers spécifiques ainsi que des répertoires entiers. La comparaison n'inclut pas les méta-informations.

Si vous avez besoin d'une solution continue, winscp est un client ftp qui peut être configuré pour comparer constamment les répertoires. Il existe probablement d'autres clients ftp qui peuvent faire cela. Et vous pouvez déplacer ou traiter des fichiers par programmation en utilisant PowerShell ou quelque chose de similaire.

Je me rends compte que ce n'est pas une réponse python, mais vous faites peut-être un travail inutile en essayant de le coder vous-même (et vous ne pourrez pas faire un meilleur travail).


3 commentaires

Ces solutions ne font-elles pas plus que comparer et fusionner les répertoires pour que le répertoire "cible" soit mis à jour avec tous les fichiers qui se trouvent dans le répertoire "source"? Cela peut être bien plus que ce que l'OP est prêt à mâcher.


@Booboo - winmerge est manuel, vous pouvez donc faire ce que vous voulez manuellement. winscp peut être automatisé pour pousser / déplacer / écraser / alerter selon ce qui est prévu.


En regardant la comparaison des dossiers de winmerge, elle semble être basée sur les dates et les tailles des fichiers (pas ce que veut l'OP). La commande de synchronisation de WinScp a une option -preview , mais pour autant que je -preview elle est également basée sur des dates et / ou des tailles en fonction de la façon dont vous définissez l'option -criteria ; Je ne pense pas que WinScp puisse faire une comparaison octet par octet du contenu des fichiers.



4
votes

Il existe plusieurs façons de comparer les fichiers .csv entre les 2 référentiels (système de fichiers serveur et système de fichiers local).


Méthode 1: utiliser hashlib

Cette méthode utilise le module Python hashlib. J'ai utilisé l'algorithme de hachage sha256 pour calculer le condensé de hachage des fichiers. Je compare les hachages des fichiers avec le nom exact du fichier. Cette méthode fonctionne bien, mais elle ignorera tout fichier qui n'existe pas dans les deux répertoires.

 import fs
 import os
 import filecmp
 from google.cloud import storage

 def create_temp_memory_filesystem():
    mem_fs = fs.open_fs('mem://')
    virtual_disk = mem_fs.makedir('hidden_dir')
    return mem_fs, virtual_disk

 def query_google_cloud_storage_file_by_name(filename, memory_filesystem, temp_directory):
   client = storage.Client.from_service_account_json('path_to_your_credentials.json')
   bucket = client.get_bucket('your_bucket_name')
   blobs = bucket.list_blobs()
   for blob in blobs:
      if blob.name == filename:
        with memory_filesystem.open(f'{temp_directory}/{filename}', 'w') as f:
            f.write(str(blob.download_to_filename(blob.name)))
            f.close()

 def compare_local_files_to_google_storage_files(local_csv_files):
   virtual_disk = create_temp_memory_filesystem()
   directory_name = str(virtual_disk[1]).split('/')[1]
   files = set(os.listdir(local_csv_files))
   for filename in files:
      if filename.endswith('.csv'):
        local_file = f'{local_csv_files}/{filename}'
        query_google_cloud_storage_file_by_name(filename, virtual_disk[0], directory_name)
        virtual_files = virtual_disk[0].opendir(directory_name)
        for file_name in virtual_files.listdir('/'):
          comparison = filecmp.cmp(local_file, file_name, shallow=False)
          if comparison:
            print(f'The file - {filename} is identical in both the local file system and the Google Cloud bucket.')
          elif not comparison:
                print(f'The file - {filename} is different between the local file system and the Google Cloud bucket.')
           virtual_files.remove(file_name)
   virtual_disk[0].close()

Méthode 2: utiliser os st_size

Cette méthode utilise le module Python os. Dans cet exemple, j'ai comparé la taille des fichiers. Cette méthode fonctionne bien, mais elle classera mal tout fichier dont les données sont modifiées et ne modifient pas la taille du fichier.

import fs
import os
import hashlib
from google.cloud import storage

def create_temp_memory_filesystem():
   mem_fs = fs.open_fs('mem://')
   virtual_disk = mem_fs.makedir('hidden_dir')
   return mem_fs, virtual_disk

def query_google_cloud_storage_file_by_name(filename, memory_filesystem, temp_directory):
  client = storage.Client.from_service_account_json('path_to_your_credentials.json')
  bucket = client.get_bucket('your_bucket_name')
  blobs = bucket.list_blobs()
  for blob in blobs:
     if blob.name == filename:
       with memory_filesystem.open(f'{temp_directory}/{filename}', 'w') as f:
           f.write(str(blob.download_to_filename(blob.name)))
           f.close()

def compare_local_files_to_google_storage_files(local_csv_files):
   virtual_disk = create_temp_memory_filesystem()
   directory_name = str(virtual_disk[1]).split('/')[1]
   files = set(os.listdir(local_csv_files))
   for filename in files:
      if filename.endswith('.csv'):
        local_file_hash = hashlib.sha256(open(f'{local_csv_files}/{filename}', 'rb').read()).hexdigest()
        query_google_cloud_storage_file_by_name(filename, virtual_disk[0], directory_name)
        virtual_files = virtual_disk[0].opendir(directory_name)
        for file_name in virtual_files.listdir('/'):
            gs_file_hash = hashlib.sha256(open(file_name, 'rb').read()).hexdigest()
            if local_file_hash == gs_file_hash:
                print(f'The file - {filename} is identical in both the local file system and the Google Cloud bucket.')
            elif local_file_hash != gs_file_hash:
                print(f'The file - {filename} is different between the local file system and the Google Cloud bucket.')
            virtual_files.remove(file_name)
    virtual_disk[0].close()

Méthode 3: utiliser os st_size et st_mtime

Cette méthode utilise également le module Python os. Dans cet exemple, j'ai comparé non seulement la taille du fichier, mais également l'heure de la dernière modification. Cette méthode fonctionne bien, mais elle classera à tort les fichiers comme étant identiques. Lors des tests, j'ai enregistré un fichier sans modifications de données et os.st_mtime a marqué le fichier comme étant différent, mais en réalité ce n'était pas vraiment différent.

import fs
import os
import boto3
import filecmp

def create_temp_memory_filesystem():
   mem_fs = fs.open_fs('mem://')
   virtual_disk = mem_fs.makedir('hidden_dir')
   return mem_fs, virtual_disk

def query_s3_file_by_name(filename, memory_filesystem, temp_directory):
   s3 = boto3.resource('s3', aws_access_key_id='your_access_key_id',
                    aws_secret_access_key='your_secret_access_key')
   bucket = s3.Bucket('your_bucket_name')
   for obj in bucket.objects.all():
      if obj.key == filename:
        body = obj.get()['Body'].read()
        with memory_filesystem.open(f'{temp_directory}/s3_{filename}', 'w') as f:
            f.write(str(body))
            f.close()

def compare_local_files_to_s3_files(local_csv_files):
   virtual_disk = create_temp_memory_filesystem()
   directory_name = str(virtual_disk[1]).split('/')[1]
   files = set(os.listdir(local_csv_files))
   for filename in files:
      if filename.endswith('.csv'):
        local_file = f'{local_csv_files}/{filename}'
        query_s3_file_by_name(filename, virtual_disk[0], directory_name)
        virtual_files = virtual_disk[0].opendir(directory_name)
        for file_name in virtual_files.listdir('/'):
            comparison = filecmp.cmp(local_file, file_name, shallow=False)
            if comparison:
                print(f'The file - {filename} is identical in both the local file system and the S3 bucket.')
            elif not comparison:
                print(f'The file - {filename} is different between the local file system and the S3 bucket.')
            virtual_files.remove(file_name)
   virtual_disk[0].close()

Méthode 4: utilisation de set ()

Cet exemple utilise Python set () pour déterminer les différences ligne à ligne entre 2 fichiers csv du même nom. Cette méthode affichera le changement exact entre les 2 fichiers csv.

import fs
import os
import boto3
import hashlib

def create_temp_memory_filesystem():
   mem_fs = fs.open_fs('mem://')
   virtual_disk = mem_fs.makedir('hidden_dir')
   return mem_fs, virtual_disk

def query_s3_file_by_name(filename, memory_filesystem, temp_directory):
   s3 = boto3.resource('s3', aws_access_key_id='your_access_key_id',
                    aws_secret_access_key='your_secret_access_key')
   bucket = s3.Bucket('your_bucket_name')
   for obj in bucket.objects.all():
      if obj.key == filename:
        body = obj.get()['Body'].read()
        with memory_filesystem.open(f'{temp_directory}/s3_{filename}', 'w') as f:
            f.write(str(body))
            f.close()

 def compare_local_files_to_s3_files(local_csv_files):
    virtual_disk = create_temp_memory_filesystem()
    directory_name = str(virtual_disk[1]).split('/')[1]
    files = set(os.listdir(local_csv_files))
    for filename in files:
       if filename.endswith('.csv'):
         local_file_hash = hashlib.sha256(open(f'{local_csv_files}/{filename}', 'rb').read()).hexdigest()
         query_s3_file_by_name(filename, virtual_disk[0], directory_name)
         virtual_files = virtual_disk[0].opendir(directory_name)
         for file_name in virtual_files.listdir('/'):
            s3_file_hash = hashlib.sha256(open(file_name, 'rb').read()).hexdigest()
            if local_file_hash == s3_file_hash:
                print(f'The file - {filename} is identical in both the local file system and the S3 bucket.')
            elif local_file_hash != s3_file_hash:
                print(f'The file - {filename} is different between the local file system and the S3 bucket.')
            virtual_files.remove(file_name)
    virtual_disk[0].close()

Méthode 5: utilisation de filecmp.cmp

Cette méthode utilise le module Python filecmp. Dans cet exemple, j'ai utilisé filecmp.cmp avec shallow défini sur False . La définition de ce paramètre sur False indique à filecmp d'examiner le contenu des fichiers et non les métadonnées, telles que la taille du fichier, qui est la valeur par défaut pour filecmp.cmp. Cette méthode fonctionne aussi bien que la méthode 1, qui utilisait hashlib.

import csv

def get_csv_file_lines(file):
   with open(file, 'r', encoding='utf-8') as csv_file:
      rows = csv.reader(csv_file)
      for row in rows:
         yield row

def compare_csv_files_line_by_line(csv_file_one, csv_file_two):
   csvfile_02 = get_csv_file_lines(csv_file_two)
   for line_one in get_csv_file_lines(csv_file_one):
      line_two = csvfile_02.__next__()
      if line_two != line_one:
        print('File names being compared:')
        print(f'csv_file_one: {csv_file_one}')
        print(f'csv_file_two: {csv_file_two}')
        print(f'The following rows have difference in the files being compared.')
        print('csv_file_one:', line_one)
        print('csv_file_two:', line_two)
        print('\n')

Méthode 6: utilisation de filecmp.dircmp

Cette méthode utilise également le module Python filecmp. Dans cet exemple, j'ai utilisé filecmp.dircmp , qui me permet non seulement d'identifier les fichiers qui ne sont pas communs entre les 2 répertoires et de trouver les fichiers qui ont des noms similaires, mais un contenu différent.

import filecmp

def directory_recursive(directory_one, directory_two):
   files = filecmp.dircmp(directory_one, directory_two)
   for filename in files.diff_files:
      print(f'The file - {filename} is different in the directories - {files.left} and {files.right}')
   for filename in files.left_only:
      print(f'The file - {filename} - was only found in the directory {files.left}')
   for filename in files.right_only:
      print(f'The file - {filename} - was only found in the directory {files.right}')

Méthode 7: comparaison ligne par ligne

Cet exemple effectue une comparaison ligne par ligne de 2 fichiers csv et affiche la ligne différente. La sortie peut être ajoutée au dictionnaire Python ou au fichier JSON pour le secondaire.

import filecmp

def compare_common_files(directory_one, directory_two):
  d1_files = set(os.listdir(directory_one))
  d2_files = set(os.listdir(directory_two))
  common_files = list(d1_files & d2_files)
  if common_files:
    for filename in common_files:
        file_01 = f'{directory_one}/{filename}'
        file_02 = f'{directory_two}/{filename}'
        comparison = filecmp.cmp(file_01, file_02, shallow=False)
        if comparison:
            print(f'The file - {filename} is identical in the directories - {directory_one} and {directory_two}')
        elif not comparison:
            print(f'The file - {filename} is different in the directories - {directory_one} and {directory_two}')

Système de fichiers local vers un compartiment S3 à l'aide de hashlib

L'exemple ci-dessous est un cas d'utilisation réel pour comparer des fichiers entre un système de fichiers local et un compartiment S3 distant. À l'origine, j'allais utiliser object.e_tag créé par AWS S3, mais cette balise peut avoir des problèmes et ne doit pas être utilisée dans une opération de comparaison de hachage. J'ai décidé d'interroger S3 et de charger un fichier individuel dans un système de fichiers mémoire qui pourrait être interrogé et vidé lors de chaque opération de comparaison. Cette méthode a très bien fonctionné et n'a aucun impact négatif sur les performances de mon système.

import os

def compare_common_files_by_lines(directory_one, directory_two):
   d1_files = set(os.listdir(directory_one))
   d2_files = set(os.listdir(directory_two))
   common_files = list(d1_files & d2_files)
   if common_files:
     for filename in common_files:
        if fileName.endswith('.csv'):
          file_01 = open(f'{directory_one}/{filename}', 'r', encoding='ISO-8859-1')
          file_02 = open(f'{directory_two}/{filename}', 'r', encoding='ISO-8859-1')
          csv_file_01 = set(map(tuple, csv.reader(file_01)))
          csv_file_02 = set(map(tuple, csv.reader(file_02)))
          different = csv_file_01 ^ csv_file_02
            for row in sorted(different, key=lambda x: x, reverse=True):
               if row:
                  print(f'This row: \n {row} \n was different between the file {fileName} in the directories'
                          f' {directory_one} and {directory_two}')

Système de fichiers local vers le compartiment S3 à l'aide de filecmp

Cet exemple est le même que celui ci-dessus, sauf que j'utilise filecmp.cmp au lieu de hashlib pour l'opération de comparaison.

import os

 def compare_common_files_by_metadata(directory_one, directory_two):
   d1_files = set(os.listdir(directory_one))
   d2_files = set(os.listdir(directory_two))
   common_files = list(d1_files & d2_files)
   if common_files:
     for filename in common_files:
        file_01 = os.stat(f'{directory_one}/{filename}')
        file_02 = os.stat(f'{directory_two}/{filename}')
        if file_01.st_size == file_02.st_size and file_01.st_mtime == file_02.st_mtime:
            print(f'The file - {filename} is identical in the directories {directory_one} and {directory_two}')
        elif file_01.st_size != file_02.st_size or file_01.st_mtime != file_02.st_mtime:
            print(f'The file - {filename} is different in the directories {directory_one} and'
                  f' {directory_two}')

Système de fichiers local vers le bucket de stockage Google Cloud à l'aide de hashlib

Cet exemple est similaire à l'exemple de code hashlib S3 ci-dessus, mais il utilise un compartiment de stockage Google Cloud.

import os 

def compare_common_files_by_size(directory_one, directory_two):
  d1_files = set(os.listdir(directory_one))
  d2_files = set(os.listdir(directory_two))
  common_files = list(d1_files &  d2_files)
  if common_files:
    for filename in common_files:
       file_01 = os.stat(f'{directory_one}/{filename}')
       file_02 = os.stat(f'{directory_two}/{filename}')
       if file_01.st_size == file_02.st_size:
            print(f'The file - {filename} is identical in the directories {directory_one} and {directory_two}')
       elif file_01.st_size != file_02.st_size:
            print(f'The file - {filename} is different in the directories {directory_one} and'
                  f' {directory_two}')

Système de fichiers local vers le bucket de stockage Google Cloud à l'aide de filecmp

Cet exemple est similaire à l'exemple de code S3 filecmp ci-dessus, mais il utilise un bucket de stockage Google Cloud.

import hashlib

def compare_common_files_by_hash(directory_one, directory_two):
   d1_files = set(os.listdir(directory_one))
   d2_files = set(os.listdir(directory_two))
   common_files = list(d1_files &  d2_files)
   if common_files:
     for filename in common_files:
        hash_01 = hashlib.sha256(open(f'{directory_one}/{filename}', 'rb').read()).hexdigest()
        hash_02 = hashlib.sha256(open(f'{directory_two}/{filename}', 'rb').read()).hexdigest()
        if hash_01 == hash_02:
            print(f'The file - {filename} is identical in the directories {directory_one} and {directory_two}')
        elif hash_01 != hash_02:
            print(f'The file - {filename} is different in the directories {directory_one} and {directory_two}')


0 commentaires

2
votes

shallow (facultatif): Une valeur booléenne «Vrai» ou «Faux». La valeur par défaut de ce paramètre est True. Si sa valeur est True, seules les métadonnées des fichiers sont comparées. Si False, le contenu des fichiers est comparé.

import filecmp   
  
# Path of first file 
file1 = "/home/geeks/Desktop/gfg/data.txt"
  
# Path of second file 
file2 = "/home/geeks/Desktop/gfg/gfg.txt"
   
# Compare the os.stat() 
# signature i.e the metadata 
# of both files  
comp = filecmp.cmp(file1, file2) 
  
# Print the result of comparison 
print(comp) 
  
# Compare the 
# contents of both files 
comp = filecmp.cmp(file1, file2, shallow = False) 
  
# Print the result of comparison 
print(comp)

https://www.geeksforgeeks.org/python-filecmp-cmp-method/#:~:text=cmp()%20method%20in%20Python,size%2C%20date%20modified%20etc. )


0 commentaires

2
votes

Le problème est que filecmp.dircmp effectue une comparaison superficielle :

La classe dircmp compare les fichiers en effectuant des comparaisons superficielles comme décrit pour filecmp.cmp ()

Une comparaison superficielle signifie que filecmp vérifiera si le fichier A et le fichier B os.stat sont égaux. Dans ce cas, il renvoie vrai. Si faux, il compare ensuite les contenus A et B et retourne vrai s'ils sont égaux, et faux dans le cas contraire.


Pour ignorer os.stat , vous pouvez utiliser filecmp.cmpfiles(dir1, dir2, common, shallow=False) . Notez que filecmp.cmpfiles fonctionne comme suit:

Comparez les fichiers des deux répertoires dir1 et dir2 dont les noms sont donnés par common .

Vous pouvez en savoir plusici .


De plus, vous pouvez parcourir en boucle tous les fichiers à l'intérieur de dir1 et dir2 , et pour chacun d'eux exécutez filecmp.cmp(f1, f2, shallow=False) . Vous pouvez en savoir plus sur filecmp.cmp ici .


Si vous avez des doutes sur la façon dont fonctionne peu profond , cette réponse pourrait vous aider.


0 commentaires

1
votes

C'est rapide, sale et gourmand en ressources;) Si vous êtes sous Linux, appelez diff , si vous êtes sous Windows, appelez fc . Autrement dit, si vous voulez simplement savoir s'ils ont les mêmes données. Vous devez être en mesure d'accéder aux fichiers `` à partir du serveur '' localement, alors téléchargez-les et mettez-les dans un bac une fois que vous avez comparé - ou montez un lecteur partagé si cela est possible. Comme vous allez comparer les données partout où votre code s'exécute, vous devez soit télécharger vos données, soit télécharger les données du serveur de toute façon, alors tirez-les simplement vers le bas et mettez-les dans le bac lorsque vous avez terminé. par exemple sous windows:

import subprocess
def files_are_a_match(file1, file2):
"""function returns True for matching files. False on mismatch or error. Expects windows file paths as strings"""
 try:
  cmd = f"fc /B \"{file1}\" \"{file2}\""
  txt = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
 except:
  return False
 return "FC: no differences encountered" in str(txt)

Une meilleure approche pour obtenir un «n'est-ce pas la même chose? La réponse serait de générer un hachage des fichiers, si vous avez le contrôle du serveur, vous le feriez là-bas, et le vôtre localement, puis comparez les hachages (moins de données volantes). Mais ce n'est pas clair quelle est votre intention ou quel est votre contrôle sur le serveur.


0 commentaires

1
votes

Voici une façon de comparer le contenu des fichiers CSV.

  • Créez un dictionnaire avec les noms de fichiers comme clés et les hachages SHA comme valeurs.
  • Faites cela sur les machines locales et distantes.
  • Comparez les dictionnaires (des contenus identiques auront des hachages identiques).

Importez des packages et créez deux fonctions:

local_dir = Path('../../../projects')

start = perf_counter()
local_hashes = csv_hashes(local_dir)
elapsed = perf_counter() - start

rate = len(local_hashes) / elapsed
print(f'indexed {rate:.3f} files/sec')

indexed 53.342 files/sec  ## too slow for real-world use case?

Spécifiez le répertoire de niveau supérieur et créez le nom du fichier: valeur de hachage dict sur la machine locale.

import hashlib
from pathlib import Path
from time import perf_counter

def sha256sum(filename):
    ''' source:  https://stackoverflow.com/a/44873382/13608599 '''
    h  = hashlib.sha256()
    b  = bytearray(128 * 1024)
    mv = memoryview(b)
    with open(filename, 'rb', buffering=0) as f:
        for n in iter(lambda : f.readinto(mv), 0):
            h.update(mv[:n])
    return h.hexdigest()

def csv_hashes(dir_name):
    ''' Map CSV filenames to SHA hashes. '''
    return { csv_file: sha256sum(csv_file)
             for csv_file in dir_name.rglob('*.csv') }

Répétez sur la machine distante et comparez les deux dictionnaires.


0 commentaires

0
votes

Ce programme utilise le package pysftp du référentiel PyPI . Il parcourt récursivement le répertoire local à la recherche de fichiers csv. Pour chaque fichier trouvé, il calcule le chemin dans le répertoire distant du fichier csv distant correspondant et l'utilisation de pysftp teste d'abord si le fichier distant existe ou non. Si le fichier existe, le fichier est lu. pysftp (et le protocole sftp 3 en général) ne prend en charge que la lecture binaire. On suppose donc que le contenu du fichier peut être décodé en utilisant utf-8 . Les fichiers locaux et distants sont «normalisés» pour tenir compte du fait que différentes conventions de fin de ligne peuvent être utilisées pour les deux fichiers si différentes plates-formes OS sont utilisées avant d'être comparées. Les fichiers sont ensuite comparés pour l'égalité. Vous pouvez, bien entendu, modifier la façon dont la sortie doit être affichée.

#!/usr/bin/env python3

import pysftp
import sys
from pathlib import Path
from io import BytesIO
import re

LOCAL_DIR = 'C:\\My\\Directory\\' # with closing separator
REMOTE_DIR = '/home/directory/' # absolute directory with closing separator


class Sftp:
    def __init__(self, host, port, username, password, deploymentDirectory, verbose=True):
        if deploymentDirectory[-1] != '/': deploymentDirectory += '/'
        self.deployment_directory = deploymentDirectory
        self.verbose = verbose
        self.connection = None
        try:
            self.connection = pysftp.Connection(host, port=port, username=username, password=password)
        except Exception:
            print('Could not connect to remote sftp server with the specified arguments.', file=sys.stderr)
            sys.exit(1)

    def __del__(self):
        self.close()

    def close(self):
        if self.connection:
            self.connection.close()
            self.connection = None

    def read_text_file(self, remote_file_name):
        full_remote_file_name = self.deployment_directory + remote_file_name
        b = BytesIO()
        self.connection.getfo(full_remote_file_name, b)
        s = b.getvalue().decode('utf-8')
        return s


    def remote_file_exists(self, remote_file_name):
        full_remote_file_name = self.deployment_directory + remote_file_name
        return self.connection.isfile(full_remote_file_name)


def compare(local_text, remote_text):
    """
    The files could be the same except for the way the hosts handle the line-termination sequence (Windows: \r\n, Unix/Linux: \n, Mac: \r).
    So, let's normalize:
    """
    rex = re.compile(r'\r\n?')
    local_text = rex.sub('\n', local_text)
    remote_text = rex.sub('\n', remote_text)
    return local_text == local_text


def main():
    sftp = Sftp(host='demo.com', port=22, username='xxxx', password='xxxx', deploymentDirectory=REMOTE_DIR)
    l_local_dir = len(LOCAL_DIR)
    for path in Path(LOCAL_DIR).rglob('*.csv'):
        dir, file_name = path.parent, path.name
        # compute relative remote path:
        remote_file_name = str(dir)[l_local_dir:].replace('\\', '/') + '/' + file_name
        if not sftp.remote_file_exists(remote_file_name):
            print(f'{path}: This file does not exist in remote directory.')
        else:
            remote_text = sftp.read_text_file(remote_file_name)
            with path.open(encoding='utf-8') as f:
                local_text = f.read()
                if compare(local_text, remote_text):
                    print(f'{path} exits in the remote directory and matches.')
                else:
                    print(f'{path} exits in the remote directory but does not match.')
    sftp.close()


main()


0 commentaires