【问题标题】:Compare two lists of filenames + size+ modification time比较两个文件名列表 + 大小 + 修改时间
【发布时间】:2021-06-26 22:01:22
【问题描述】:

对于一个学生项目,我构建了一个脚本,仅将“新”文件复制到遥远的目录。 我能够找到远程文件夹中不存在的文件并将其复制,它正在工作。 现在,我尝试复制具有相同名称但大小或修改日期不同的文件。

我可以创建一个包含所有需要信息的本地文件列表:

localfilestime = os.scandir(_SOURCE)

print("fichiers sur répertoire source: \n ")

for file in localfilestime:

    localfilestime = (file.stat().st_size, file.stat().st_mtime, file.name)
    print (localfilestime)

远方的也一样:

ssh = paramiko.SSHClient()

ssh.set_missing_host_key_policy( paramiko.AutoAddPolicy() )
ssh.connect(_RMTHOST, username=_USR, password=_DESTPASS)

ftp = ssh.open_sftp()
distfilestime = ftp.listdir_attr(_DEST)

print("fichiers sur répertoire de destination: \n")
for f in distfilestime:
    distfilestime =(f.st_size,f.st_mtime, f.filename)
    print (distfilestime)

我得到两个排序列表,但我有两个问题:

fichiers sur répertoire source: 
 
(915, 1622736305.82416, '20_060_12C_24T_3800_AMD_Ryzen_9_3900X_12_Core_Processor_64_Bit.txt')
(9602, 1624743218.308332, 'blabla.txt')
(143897, 1583941480.0, 'Projet4_configuration_navigateur_proxy01.jpg')
(0, 1624520128.9788647, 'testcopie.txt')
**********************************************************************************************************

fichiers sur répertoire de destination: 

(915, 1622736305, '20_060_12C_24T_3800_AMD_Ryzen_9_3900X_12_Core_Processor_64_Bit.txt')
(37, 1624738406, 'blabla.txt')
(143897, 1583941480, 'Projet4_configuration_navigateur_proxy01.jpg')
(0, 1624520128, 'testcopie.txt')
  1. 本地列表中的 mtime 返回一个浮点数,而不是远距离的,我认为无法正确比较这些数字。

  2. 我找不到一个可行的解决方案来比较这些文件列表,我尝试了很多解决方案,以及“不太糟糕”的解决方案:

    def get_difference(list_a, list_b):
        return set(list_a) - set(list_b)
    
    
    list_a = localfilestime
    list_b = distfilestime
    
    non_match = list(get_difference(list_a, list_b))
    print("No match elements: ", set(non_match))
    

    只返回一个未修改的文件mtime,而不是像“blabla.txt”这样大小不同的文件:

    No match elements:  {1624520128.9788647}
    

我确定我在某处犯了错误,也许我的方法不好。有人可以帮我解答这个问题吗?

编辑:这是整个脚本,在 2 个局域网服务器之间的 Win10 上使用 (1 个物理的,1 个在私有共享网络上的 Virtualbox)脚本调用配置文件。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import socket
import paramiko
import pysftp
import shutil




# importation des variables globales
from configuration import _USR, _RMTHOST, _DESTPASS, _PORT, _SOURCE, _DEST, _EXCLUSION,  _TIMEDATE, _OUTPUT

#Définition de la variable globale nécessaire à la création du dussier temporaire de copie
_SOURCE_TEMP = (_SOURCE + "temp")

#effacer dossier temporaire en cas d'erreur du script précédent
if os.path.exists(_SOURCE_TEMP):
    shutil.rmtree(_SOURCE_TEMP)


# énoncé du script

print("\ncopie miroir du dossier: " + _SOURCE + "  vers:" + _RMTHOST +(": ") + _DEST)

print("\n**********************************************************************************************************\n")

# vérifier présence du fichier de configuration
try:
    with open('configuration.py'):
        pass
        print("le fichier de configuration est présent et valide")
except IOError:
    sys.exit("Erreur: le fichier de configuration n’est pas present, annulation du backup")

print("\n**********************************************************************************************************\n")


# Vérifier l'accès au fichier source

if os.path.exists(_SOURCE):
    print("le dossier source: ", _SOURCE, "est valide et accessible \n \n")
else:
    print("le dossier source: ", _SOURCE, "n\’est pas valide/existant, annulation du backup \n \n")
    sys.exit()

print("**********************************************************************************************************\n")
# test de connexion vers le serveur distant

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    try:
        s.connect((_RMTHOST, _PORT))
        s.sendall(b'Hello, world')
        data = s.recv(1024)
        print('communication réussie avec le serveur ' + _RMTHOST, repr(data) + ('\n \n'))

    except Exception as ex:
        print(
            "impossible de se connecter au serveur, vérifiez vos identifiants\n \n************************ \n\nannulation de la sauvegarde")
        sys.exit()

print("**********************************************************************************************************\n")

# création de liste de fichiers locaux

localfilestime = os.scandir(_SOURCE)


print("fichiers sur répertoire source: \n ")

for f in localfilestime:

    localfilestime = (f.stat().st_size, f.stat().st_mtime, f.name)

    print (localfilestime)



print("**********************************************************************************************************\n")

#connexion pour listing fichiers présents répertoire distant
ssh = paramiko.SSHClient()

# importation auto de clé de connexion avec le client
ssh.set_missing_host_key_policy( paramiko.AutoAddPolicy())
ssh.connect(_RMTHOST, username=_USR, password=_DESTPASS)

ftp = ssh.open_sftp()

distfilestime = ftp.listdir_attr(_DEST)

print("fichiers sur répertoire de destination: \n")
for f in distfilestime:
    distfilestime =(f.st_size,f.st_mtime, f.filename)
    print (distfilestime)


#comparaison éléments

loc_files = {}
for f in localfilestime:
    size = f.stat().st_size
    time = int(f.stat().st_mtime)
    name = f.name
    loc_files[(size, time, name)] = f

srv_files = {}
for f in distfilestime:
    size = f.st_size
    time = f.st_mtime
    name = f.filename
    srv_files[(size, time, name)] = f


# get lists of unique files based on keys (size,time,name) of these dicts

loc_uniq = [ loc_files[x] for x in loc_files if x not in srv_files ]
srv_uniq = [ srv_files[x] for x in srv_files if x not in loc_files ]

print(loc_uniq)

print("\n**********************************************************************************************************\n")

# création du répertoire temporaire pour copie
_SOURCE_TEMP = (_SOURCE + "temp")

if not os.path.exists(_SOURCE_TEMP):
    os.makedirs(_SOURCE_TEMP, exist_ok=True)

#clôture de la connexion avec le serveur distant
ssh.close()
ftp.close()

#copie des fichiers absents du répertoire distant sur le dossier temporaire pour transfert

os.chdir(_SOURCE)

for files in loc_uniq:
    shutil.copy2(files, _SOURCE_TEMP)

#retirer fichiers à exclure
os.chdir(_SOURCE_TEMP)
files = [f for f in os.listdir('.') if os.path.isfile(f)]

for f in files:
    if f.endswith(_EXCLUSION):
        print(f, "ne sera pas copié selon la liste d'exclusion")
        os.remove(f)

os.chdir(_SOURCE)

print("\n**********************************************************************************************************\n")

#copie des fichiers dans le répertoire distant

with pysftp.Connection(host=_RMTHOST, username=_USR, password=_DESTPASS) as sftp:
    sftp.put(_SOURCE_TEMP, _DEST, preserve_mtime=True)

    sftp.close()

if os.path.exists(_SOURCE_TEMP):
    shutil.rmtree(_SOURCE_TEMP)

配置文件:

import time

_EXCLUSION = '.ini'
_SOURCE = "C:\\Users\\XXX\\testp6\\"
_OUTPUT = _SOURCE + time.strftime('%Y_%m_%d-%Hh%Mmin%Ss')
_TIMEDATE = time.strftime('%Y_%m_%d-%Hh%Mmin%Ss')
_DEST = "C:\\Users\\XXXx\\test_distant_p6\\"
_USR = 'XXX'
_RMTHOST ='192.168.56.103'
_DESTPASS='XXX'
_PORT=22

以及实际回报:

copie miroir du dossier: C:\Users\Jerem\testp6\  vers:192.168.56.103: C:\Users\turmix\test_distant_p6\

**********************************************************************************************************

le fichier de configuration est présent et valide

**********************************************************************************************************

le dossier source:  C:\Users\Jerem\testp6\ est valide et accessible 
 

**********************************************************************************************************

communication réussie avec le serveur 192.168.56.103 b'SSH-2.0-OpenSSH_for_Windows_8.1\r\n'
 

**********************************************************************************************************

fichiers sur répertoire source: 
 
(915, 1622736305.82416, '20_060_12C_24T_3800_AMD_Ryzen_9_3900X_12_Core_Processor_64_Bit.txt')
(9602, 1624743218.308332, 'blabla.txt')
(46, 1624736892.7385924, 'desktop.ini')
(143897, 1583941480.0, 'Projet4_Jeremie_Voita_configuration_navigateur_proxy01.jpg')
(0, 1624520128.9788647, 'testcopie.txt')
**********************************************************************************************************

fichiers sur répertoire de destination: 

(915, 1622736305, '20_060_12C_24T_3800_AMD_Ryzen_9_3900X_12_Core_Processor_64_Bit.txt')
(37, 1624738406, 'blabla.txt')
(143897, 1583941480, 'Projet4_Jeremie_Voita_configuration_navigateur_proxy01.jpg')
(0, 1624520128, 'testcopie.txt')
Traceback (most recent call last):
  File "C:\Users\jerem\OneDrive\P6\pythonP6\dernierendate.py", line 118, in <module>
    size = f.stat().st_size
AttributeError: 'int' object has no attribute 'stat'

Process finished with exit code 1

【问题讨论】:

  • 1.为什么不直接使用 SSH 运行 python 命令呢?您通过 SSH 连接,然后调用 SFTP 函数来检索统计信息,这听起来有点矫枉过正。 2. 比较建议:可以使用字典,而Key就是文件名,每个key根据文件大小可能是另一个字典。因此,Miss 意味着文件存在差异或它们不存在。让我知道您是否可以按照上述建议继续前进
  • 我可能在目录中有大量文件,我认为通过 SSh 会更慢?另外,我尝试了这个解决方案,但是我不能使用带有遥远目录的基本比较模块,它总是返回错误。当然我认为有些解决方案是存在的,但我是初学者,我试图将我的脚本保留为尽可能简单。我会研究一下字典,谢谢你的建议

标签: python list file difference


【解决方案1】:

它可以通过多种方式完成。例如这种方式:

localfilestime = [
    (915, 1622736305.82416, '20_060_12C_24T_3800_AMD_Ryzen_9_3900X_12_Core_Processor_64_Bit.txt'),
    (9602, 1624743218.308332, 'blabla.txt'),
    (143897, 1583941480.0, 'Projet4_configuration_navigateur_proxy01.jpg'),
    (0, 1624520128.9788647, 'testcopie.txt'),
]

distfilestime = [
    (915, 1622736305, '20_060_12C_24T_3800_AMD_Ryzen_9_3900X_12_Core_Processor_64_Bit.txt'),
    (37, 1624738406, 'blabla.txt'),
    (143897, 1583941480, 'Projet4_configuration_navigateur_proxy01.jpg'),
    (0, 1624520128, 'testcopie.txt'),
]

# convert float dates into integer dates
localfilestime = [(size, int(date), name) for (size, date, name) in localfilestime]

# convert every element of the lists into a { key:value }
localfilestime = { str(x):x for x in localfilestime }
distfilestime  = { str(x):x for x in distfilestime  }

# get unique values from the lists
localfilestime_uniques = { localfilestime[x] for x in localfilestime if x not in distfilestime  }
distfilestime_uniques  = { distfilestime[x]  for x in distfilestime  if x not in localfilestime }

print(localfilestime_uniques) # {(9602, 1624743218, 'blabla.txt')}
print(distfilestime_uniques)  # {(37, 1624738406, 'blabla.txt')}

更新

可能你需要这个:

# make the dicts { (size, time, name) : file }

loc_files = {}
for f in localfilestime:
    size = f.stat().st_size
    time = int(f.stat().st_mtime)
    name = f.name
    loc_files[(size, time, name)] = f

srv_files = {}
for f in distfilestime:
    size = f.st_size
    time = f.st_mtime
    name = f.filename
    srv_files[(size, time, name)] = f


# get lists of unique files based on keys (size,time,name) of these dicts

loc_uniq = [ loc_files[x] for x in loc_files if x not in srv_files ]
srv_uniq = [ srv_files[x] for x in srv_files if x not in loc_files ]

理论上它应该可以工作,它应该为您提供两个包含唯一文件的列表(基于大小、时间、名称)。但我无法正确测试它,因为我没有包含您文件的 FTP 服务器。


我已经在 Windows 和 Mac 上测试了代码的本地部分,这部分工作正常:

import os
from pprint import pprint

localfilestime = os.scandir(r"c:\\temp")

loc_files = {}
for f in localfilestime:
    size = f.stat().st_size
    time = int(f.stat().st_mtime)
    name = f.name
    loc_files[(size, time, name)] = f

pprint(loc_files)

输出样本:

{(0, 1622330493, 'test - Copy.txt'): <DirEntry 'test - Copy.txt'>,
 (0, 1622330493, 'test.txt'): <DirEntry 'test.txt'>,
 (124, 1622332489, 'test.zip'): <DirEntry 'test.zip'>,
 (267, 1624901883, 'loc_files.py'): <DirEntry 'loc_files.py'>}

【讨论】:

  • 太好了,谢谢你的回答,我会尽快尝试的,看来正是我需要的。有很多可能性,对于初学者来说,不迷路并一步一步地学习是非常困难的。
  • 我上面的示例只是一个示例,您可以如何比较给定的元组列表(大小、时间、名称)。但看起来你的实际列表有点不同。因此,您有这两个包含文件的列表(具有许多文件属性的元组)。并且您需要通过其中一些属性(大小、时间、名称)来比较它们并获取不同文件的列表(具有许多文件属性的郁金香列表)?
  • 没错!然后我想将较新/较重的文件复制到遥远的目录,例如在名称中添加“1”。我想我知道如何重命名文件,但它实际上没用。如果有用,我可以将整个脚本添加到我最初的问题中。再次感谢您花时间帮助我
  • #facepalm 我忘了删除这行末尾的逗号size = f.stat().st_size,
  • 我认为问题可能是我使用 os.scandir(source) 并且它不管理像 ftp.listdir_attr 这样的列表?
【解决方案2】:

感谢@Yuri Khristich,我有一个完美的解决方案:

使用:

import os
import sys
import socket
import paramiko
import pysftp
import shutil
from pprint import pprint

#for local files

localfilestime = os.scandir(r"c:\\temp")

loc_files = {}
for f in localfilestime:
    size = f.stat().st_size
    time = int(f.stat().st_mtime)
    name = f.name
    loc_files[(size, time, name)] = f

print("fichiers sur répertoire source: \n ")
pprint(loc_files)

#for distant files

ssh = paramiko.SSHClient()

# importation auto de clé de connexion avec le client
ssh.set_missing_host_key_policy( paramiko.AutoAddPolicy())
ssh.connect(_RMTHOST, username=_USR, password=_DESTPASS)

ftp = ssh.open_sftp()

distfilestime = ftp.listdir_attr(_DEST)

dst_files = {}
for f in distfilestime:
    size = f.st_size
    time = f.st_mtime
    name = f.filename
    dst_files[(size, time, name)] = f

print("fichiers sur répertoire de destination: \n")
pprint(dst_files)

#clôture de la connexion avec le serveur distant
ssh.close()
ftp.close()

#create a list of files not present/differents(name, size, modification date) on the distant server:

loc_uniq = [ loc_files[x] for x in loc_files if x not in dst_files ]

#create a temp folder to copy the different files

_SOURCE_TEMP = (_SOURCE + "temp")

if not os.path.exists(_SOURCE_TEMP):
    os.makedirs(_SOURCE_TEMP, exist_ok=True)


#copy the files

for files in loc_uniq:
    shutil.copy2(files, _SOURCE_TEMP)

#if you want to exclude some files from the copy list

os.chdir(_SOURCE_TEMP)
files = [f for f in os.listdir('.') if os.path.isfile(f)]

for f in files:
    if f.endswith(_EXCLUSION):
        print(f, "ne sera pas copié selon la liste d'exclusion")
        os.remove(f)

#copy the files from the temp repertory to the distant 

with pysftp.Connection(host=_RMTHOST, username=_USR, password=_DESTPASS) as sftp:
    sftp.put_r(_SOURCE_TEMP, _DEST, preserve_mtime=True)

    sftp.close()

这部分脚本在 Win10 上完美运行,列表也在 OSX 上运行(由 Yuri 测试)我首先遇到了一个错误(在 size = f.stat().st_size, AttributeError: 'int' object has没有属性'stat'),因为我导入了“时间”模块,请注意这一点。 1000 次感谢你的帮助 Yuri!

【讨论】:

  • 我的脚本写完了,我上传到Github上分享了,不知道发在这里是否违反SO规则,如果是的话,给我留言,我会删除这个网址:github.com/Turmix80/Python-mirror-script
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多