代码之家  ›  专栏  ›  技术社区  ›  krock1516

如何只复制已存在的目标文件上已更改的文件内容?

  •  3
  • krock1516  · 技术社区  · 6 年前

    我有一个脚本,用于从一个位置复制到另一个位置,目录结构下的文件都是 .txt 文件夹。

    此脚本只计算源文件的大小,并且仅在文件大小不是零字节时进行复制。但是,我需要在 cron 在一定的时间间隔后复制任何递增的数据。

    因此,我需要知道如何只复制源文件上更新的文件内容,然后只更新新内容的目标,而不只是覆盖目标中已经存在的内容。

    代码:

    #!/bin/python3
    import os
    import glob
    import shutil
    import datetime
    
    def Copy_Logs():
        Info_month = datetime.datetime.now().strftime("%B")
        # The result of the below glob _is_ a full path
        for filename in glob.glob("/data1/logs/{0}/*/*.txt".format(Info_month)):
            if os.path.getsize(filename) > 0:
                if not os.path.exists("/data2/logs/" + os.path.basename(filename)):
                    shutil.copy(filename, "/data2/logs/")
    
    if __name__ == '__main__':
        Copy_Logs()
    

    我在找有没有办法用 shutil() 在途中 rsync 有效,或者如果有其他方法可以替代我的代码。

    简而言之,如果还没有复制,我只需要复制一个文件,如果源代码得到更新,那么只需要复制增量文件。

    注: 这个 Info_month = datetime.datetime.now().strftime("%B") 必须保留,因为这将按月份名称确定当前目录。

    编辑:

    如果我们可以使用的话,就有另一个原始想法 filecmp 具有 shutil.copyfile 模块来比较文件和目录,但我不知道如何将其放入代码中。

    import os
    import glob
    import filecmp
    import shutil
    import datetime
    
    def Copy_Logs():
        Info_month = datetime.datetime.now().strftime("%B")
        for filename in glob.glob("/data1/logs/{0}/*/*.txt".format(Info_month)):
            if os.path.getsize(filename) > 0:
                if not os.path.exists("/data2/logs/" + os.path.basename(filename)) or not filecmp.cmp("/data2/logs/" + os.path.basename(filename), "/data2/logs/"):
                    shutil.copyfile(filename, "/data2/logs/")
    
    if __name__ == '__main__':
        Copy_Logs()
    
    7 回复  |  直到 6 年前
        1
  •  1
  •   Karn Kumar    6 年前

    如上所述 rsync 在需要执行增量文件列表或说数据增量的情况下,这是一种更好的执行此类工作的方法,因此,我宁愿使用rsync和 subprocess 一直是模块。

    但是,也可以指定一个变量 Curr_date_month 要获取当前日期、月份和年份,只需从当前月份和日期文件夹中复制文件。此外,还可以定义源变量和目标变量,以便于将它们写入代码中。

    第二,尽管您有一个文件大小检查 getsize 但是我想添加一个rsync选项参数 --min-size= 以确保不复制零字节文件。

    这里是您的最终代码。

    #!/bin/python3
    import os
    import glob
    import datetime
    import subprocess
    
    def Copy_Logs():
        # Variable Declaration to get the month and Curr_date_month
        Info_month = datetime.datetime.now().strftime("%B")
        Curr_date_month = datetime.datetime.now().strftime("%b_%d_%y") 
        Sourcedir = "/data1/logs"
        Destdir = "/data2/logs/"
        ###### End of your variable section #######################
        # The result of the below glob _is_ a full path
        for filename in glob.glob("{2}/{0}/{1}/*.txt".format(Info_month, Curr_date_month, Sourcedir)):
            if os.path.getsize(filename) > 0:
                if not os.path.exists(Destdir + os.path.basename(filename)):
                    subprocess.call(['rsync', '-avz', '--min-size=1', filename, Destdir ])
    
    if __name__ == '__main__':
        Copy_Logs()
    
        2
  •  2
  •   y.luis.rojo    6 年前

    你可以使用 Google's Diff Match Patch (您可以安装它 pip install diff-match-patch )创建一个 diff 并从中应用补丁:

    import diff_match_patch as dmp_module
    
    #...
    if not os.path.exists("/data2/logs/" + os.path.basename(filename)):
        shutil.copy(filename, "/data2/logs/")
    else:
        with open(filename) as src, open("/data2/logs/" + os.path.basename(filename),
                                                                            'r+') as dst:
            dmp = dmp_module.diff_match_patch()
    
            src_text = src.read()
            dst_text = dst.read()
    
            diff = dmp.diff_main(dst_text, src_text)
    
            if len(diff) == 1 and diff[0][0] == 0:
                # No changes
                continue
    
            #make patch
            patch = dmp.patch_make(dst_text, diff)
            #apply it
            result = dmp.patch_apply(patch, dst_text)
    
            #write
            dst.seek(0)
            dst.write(result[0])
            dst.truncate()
    
        3
  •  1
  •   Peter Badida I'm Geeker    6 年前

    您需要将更改保存到某个地方,或者在文件内容更改时侦听事件。对于后者,您可以使用 watchdog .

    如果您决定真正喜欢cron而不是增量检查更改(看门狗),那么您需要将更改存储在一些数据库中。一些基本的例子是:

    ID | path        | state before cron
    1  | /myfile.txt | hello
    ...| ...         | ...
    

    然后检查 diff 将cron之前的状态转储到一个文件,运行一个简单的 diff old.txt new.txt 如果有输出(即有更改),您可以复制整个文件,或者只复制diff的输出,然后将其作为 patch 到要覆盖的文件。

    如果没有 微分 输出,文件中没有更改,因此没有要更新的内容。

    编辑: 实际上:d如果文件在同一台机器上,您甚至可能不需要数据库…这样,您就可以直接在新文件和旧文件之间进行diff+修补。

    例子:

    $ echo 'hello' > old.txt && echo 'hello' > new.txt
    $ diff old.txt new.txt                             # empty
    $ echo 'how are you' >> new.txt                    # your file changed
    $ diff old.txt new.txt > my.patch && cat my.patch  # diff is not empty now
    1a2
    > how are you
    
    $ patch old.txt < my.patch  # apply the changes to the old file
    

    在巨蟒中 old.txt new.txt 底座:

    from subprocess import Popen, PIPE
    diff = Popen(['diff', 'old.txt', 'new.txt']).communicate()[0]
    Popen(['patch', 'old.txt'], stdin=PIPE).communicate(input=diff)
    
        4
  •  1
  •   ndrwnaguib Nikkolai Fernandez    6 年前

    一种方法是将单行保存到文件中,以跟踪最近的时间(借助 os.path.getctime )您复制了这些文件,并在每次复制时维护该行。

    注意:可以优化以下代码段。

    import datetime
    import glob
    import os
    import shutil
    
    Info_month = datetime.datetime.now().strftime("%B")
    list_of_files = sorted(glob.iglob("/data1/logs/{0}/*/*.txt".format(Info_month)), key=os.path.getctime, reverse=True)
    if not os.path.exists("track_modifications.txt"):
        latest_file_modified_time = os.path.getctime(list_of_files[0])
        for filename in list_of_files:
                shutil.copy(filename, "/data2/logs/")
        with open('track_modifications.txt', 'w') as the_file:
            the_file.write(str(latest_file_modified_time))
    else:
        with open('track_modifications.txt', 'r') as the_file:
            latest_file_modified_time = the_file.readline()
        should_copy_files = [filename for filename in list_of_files if
                             os.path.getctime(filename) > float(latest_file_modified_time)]
        for filename in should_copy_files:
                shutil.copy(filename, "/data2/logs/")
    

    方法是,创建一个文件,其中包含系统修改的最新文件的时间戳。

    检索所有文件并按修改时间排序

    list_of_files = sorted(glob.iglob('directory/*.txt'), key=os.path.getctime, reverse=True)
    

    最初,在 if not os.path.exists("track_modifications.txt"): 我检查这个文件是否不存在(即第一次复制),然后将最大的文件时间戳保存到

    latest_file_modified_time = os.path.getctime(list_of_files[0])
    

    我只是复制所有给定的文件并将这个时间戳写入 track_modifications 文件。

    否则,文件就存在了(即以前复制过文件),我只需读取时间戳并将其与我读取的文件列表进行比较。 list_of_files 并检索时间戳较大的所有文件(即,在我复制的最后一个文件之后创建的文件)。那是在

    should_copy_files = [filename for filename in list_of_files if os.path.getctime(filename) > float(latest_file_modified_time)]
    

    实际上,跟踪最新修改的文件的时间戳还可以让您复制已经复制的文件。 当它们被改变时 :)

        5
  •  1
  •   Daniel Trugman    6 年前

    这方面有一些非常有趣的想法,但我会尝试提出一些新的想法。

    想法1:追踪更新的更好方法

    根据您的问题,很明显您正在使用cron作业来跟踪更新的文件。

    如果您试图监视的文件/目录数量相对较少,我建议使用另一种方法来简化您的生活。

    你可以使用Linux 使化 一种机制,允许您监视特定的文件/目录,并在文件写入时得到通知。

    赞成 :您可以立即知道每一次写入,而无需检查更改。当然,您可以编写一个处理程序,它不会每次写入都更新目标,只会在X分钟内更新一个。

    下面是一个使用 inotify python包(取自 package's page ):

    import inotify.adapters
    
    def _main():
        i = inotify.adapters.Inotify()
    
        i.add_watch('/tmp')
    
        with open('/tmp/test_file', 'w'):
            pass
    
        for event in i.event_gen(yield_nones=False):
            (_, type_names, path, filename) = event
    
            print("PATH=[{}] FILENAME=[{}] EVENT_TYPES={}".format(
                  path, filename, type_names))
    
    if __name__ == '__main__':
        _main()
    

    想法2:只复制更改

    如果你决定使用 使化 机制,跟踪您的状态是很简单的。

    然后,有两种可能性:

    1。总是追加新内容

    如果是这种情况,您可以简单地从最后一个偏移量复制任何内容,直到文件结束。

    2。新内容在随机位置写入

    在这种情况下,我建议使用其他答案提出的方法:使用diff补丁。在我看来,这是迄今为止最优雅的解决方案。

    以下是一些选项:

        6
  •  1
  •   igrinis    6 年前

    其中一个好处是 rsync 它只复制文件之间的差异。当文件变得巨大时,它会大大减少I/O。

    有过多的 远程同步 -类似于pypi中原始程序的实现和包装。这个 blog post 描述如何实现 远程同步 以一种非常好的方式在Python中使用,并且可以按原样使用。

    至于检查是否需要进行同步,您可以使用 filecmp.cmp() . 在它的浅变种中,它只检查 os.stat() 签名。

        7
  •  -2
  •   Prakruti Chandak    6 年前

    您必须集成一个数据库,并且可以根据文件的大小、名称和作者来记录文件。

    如果有任何更新,文件的大小将发生变化,您可以相应地更新或追加。