shutil.copy2 - 在不同文件系统和伪文件上的行为

2 投票
1 回答
3559 浏览
提问于 2025-04-17 07:45

我写了一个小脚本,用来同步两个文件夹。这个脚本可以同步特定类型的文件,并且可以通过文件的校验和或日期来比较文件。在比较之后,会生成一个需要复制的文件列表,然后交给一个文件复制的过程,这个过程使用了shutil.copy2()模块。

在多次测试中,我发现了shutil.copy2的一些奇怪行为,而且我没有找到解决办法:

首先:
如果源文件夹是ext3格式,而目标文件夹是fat32格式(比如U盘),shutil会报错。我认为这是因为shutil.copy2试图复制文件的元数据,但fat32不支持这个功能。不过我不知道怎么用try: except:语句来捕获这个错误。

其次:
第二个问题更复杂。源文件夹和目标文件夹都是ext3格式。在源文件夹中,有一些完整的Linux目录树的备份。当我的工具尝试同步这些目录树时,脚本会陷入一个无尽的循环,直到我的系统分区空间用完。我尝试修复这个问题,使用stat模块在开始复制之前检查源文件是否是常规文件,但这并没有帮助。出现奇怪行为的文件是/proc/661/fd/3。可能还有其他文件,但我无法测试,因为在尝试复制这个文件时,我的系统因为内存消耗而冻结。

我已经花了好几天时间寻找这两个问题的解决方案,希望这里的高手程序员们能帮我。

感谢任何帮助。

这是我文件复制过程的代码:

def _filecopy(file_list, from_dir, to_dir, overwrt):
print "Files and directories will be processed: "
for file_tupel in file_list:
    source_file_name = from_dir + file_tupel[1] + file_tupel[0]
    try:
        filemode = os.stat(source_file_name).st_mode
    except:
        filemode = 33205 
    if S_ISREG(filemode):
        target_dir_name = to_dir + file_tupel[1]
        if not os.path.isdir(target_dir_name):
            print "Create directory " + target_dir_name
            _mkdir(target_dir_name)
        target_file_name = target_dir_name + file_tupel[0]
        if os.path.isfile(target_file_name) and overwrt == "mark":
            name_appendix = "_marked_by_sync_as_different_on_" + time.strftime("%a_%d_%b_%Y_%H-%M-%S", time.localtime())
            if target_file_name.find(".") == -1:
                new_target_file_name = target_file_name + name_appendix
                new_source_file_name = source_file_name + name_appendix
            else:
                new_target_file_prefix = target_file_name.rpartition(".")[0]
                new_target_file_extension = target_file_name.rpartition(".")[2]
                new_target_file_name = new_target_file_prefix + name_appendix + "." + new_target_file_extension
                new_source_file_prefix = source_file_name.rpartition(".")[0]
                new_source_file_name = new_source_file_prefix + name_appendix + "." + new_target_file_extension
            print "Rename file " + target_file_name + " in " + new_target_file_name
            os.rename(target_file_name, new_target_file_name)
            print "Copy file " + new_target_file_name + " to " + new_source_file_name
            try:
                shutil.copy2(new_target_file_name, new_source_file_name)
            except:
                print "Could not copy " + new_target_file_name + " to " + new_source_file_name
        print "Copy file " + source_file_name + " to " + target_file_name
        try:
            shutil.copy2(source_file_name, target_file_name)
        except:
            print "Could not copy " + source_file_name + " to " + target_file_name
    else:
        print source_file_name + " seems to be no regular file and will not be copied."

在参考了答案1的提示后,shutil.copy2的语句改为:

    shutil.copyfile(source_file_name, target_file_name)
    try:
        #try to set permission bits
        shutil.copymode(new_target_file_name, new_source_file_name)
    except:
        print "Permission bits could not be copied"
    try:
        #try to copy metadata
        shutil.copystat(new_target_file_name, new_source_file_name)
    except:
        print "Metadata could not be copied"  

1 个回答

0

shutil.copy2() 实际上就是先执行 shutil.copy(),然后再执行 shutil.copystat()。为了处理你遇到的第一个问题,你可以去查看 这个链接中的 shutil.copy2() 的源代码,然后把 copystat() 的部分放在一个 try-except 块里。这样你就能从错误的追踪信息中找出应该捕获的异常。

你可能根本不想复制 /proc 文件系统。每次运行你的脚本时,最好把它排除在外。

撰写回答