使用 python 区分两个文件夹 - 具有相同的子文件夹集和文件结构

Diff two folders using python - having same set of subfolders and file structures

我正在尝试在 python 中编写一个函数来比较两个文件夹(具有完全相同的子目录结构和文件列表)。

该文件夹必然包含.c .h .txt .cat .sys .pdb,但主要集中在C 和header 文件上。

这个 diff(folder1, folder2) 的输出应该return如下

  1. 在文件夹 2

  2. 中打印新添加的 driver 文件(仅 c 和 .h) 与文件夹 1 相比,
  3. 打印删除了文件夹 2 中的 driver 个文件

(这可以通过两个杠杆来完成,用于循环存储 os.walk 结果在两个列表中,然后将它们相减)

  1. 我的挑战:diff() 函数应该开始比较所有子目录中的文件,如果它在 folder2 中发现甚至一个(c 或 h)文件被修改,它应该结束 diff 例程并且 return a FLAG = 1

例如

folder1\foo\bar.c folder2\foo\bar.c --> 相同

folder1\foo\car.c folder2\foo\car.c --> 相同

folder1\foo\dar.c folder2\foo\dar.c --> 不同 -> return flag=1

folder1\foo\far1.c folder2\foo\far1.c --> 未比较

我尝试使用 os.walk(path) 函数来执行此操作并将所有文件存储在两个单独的列表中。但是我发现这个位置的多个文件非常长和复杂。

此外,如果有一种方法可以忽略 perforce headers、注释、比较额外的间距,它会增强我的脚本

非常感谢任何建议

以下是我将要执行的操作的粗略概述:

  • 创建 set() 个文件夹 1 文件名 folder1_set
  • os.walk() 是你的朋友,加载 folder1 并开始遍历文件。
  • 对于每个文件,打开它。修改路径指向第二个文件夹,打开第二个文件并检查是否相等
  • 将文件名添加到folder1_set
  • 最后,我们必须跟踪文件夹 2 中可能丢失的文件。在文件夹 2 上使用 os.walk(),您可以保留另一个 set() 的文件名 folder2_set
  • folder1_set - folder2_set 会为您提供 folder1 中的任何项目,而不是 folder2 中的项目,反之亦然。

编辑 1

  • 要准备注释,您可以逐行阅读文件,在行中使用 .strip() 删除两端的空格,然后检查开头是否存在 /*剥离线
  • 对于 单行 注释,您还必须检查 */ 是否出现在行尾,如果出现,则将其排除。
  • 对于 多行 注释,您可以忽略所有行,直到在行尾遇到 */
  • 这也有清除空行(删除所有空格)的好处。
  • 查看 os.path.splitext 以在 walking 时轻松找到文件扩展名。

您应该能够使用 Python 的 filecmp 库完成此操作。

已编辑答案

解决@DennisNinj 的其他评论

Thanks, Is there anyway to include .c and .h for file comparison? I have more than 20 types of files in each folder and over 1000 files in each folder? – Dennis Ninj

@DennisNinj 是的,有可能,只是有点棘手。当前发布的 filecmp.dircmp 版本不支持其 "ignore" 和 "hide" 过滤器的通配符或正则表达式匹配。 (已经提交 patch 支持 dircmp 中的通配符。)所以这意味着您必须手动进行过滤。

这是一个更新的示例,可以让您更接近您想要完成的目标。 注意:请注意,由于要求在发现不同的 C 或头文件后停止方法执行,因此您可能无法获得每个 "added / deleted driver" 在比较目录中可用,因为它可能没有机会遍历所有子目录。

ccodedircomparison.py

import re
from filecmp import dircmp


def main():
    dcmp = dircmp("/Users/joeyoung/web/Whosebug/dircomparison/test1", "/Users/joeyoung/web/Whosebug/dircomparison/test2")
    if diffs_found(dcmp):
        print "FLAG = 1"


def diffs_found(dcmp):
    c_files_regex = re.compile(r".*\.[ch]$")
    deleted_drivers = []
    if len(dcmp.left_only) > 0:
        for left_only_file in dcmp.left_only:
            c_files_match = c_files_regex.match(left_only_file)
            if c_files_match:
                deleted_drivers.append(left_only_file)
        if len(deleted_drivers) > 0:
            print "Drivers deleted from {dirname}: [{deleted_drivers_list}]".format(dirname=dcmp.right, deleted_drivers_list=', '.join(deleted_drivers))
    added_drivers = []
    if len(dcmp.right_only) > 0:
        for right_only_file in dcmp.right_only:
            c_files_match = c_files_regex.match(right_only_file)
            if c_files_match:
                added_drivers.append(left_only_file)
        if len(added_drivers) > 0:
            print "Drivers added to {dirname}: [{added_drivers_list}]".format(dirname=dcmp.right, added_drivers_list=', '.join(dcmp.right_only))
    if len(dcmp.diff_files) > 0:
        differing_c_files = []
        for diff_file in dcmp.diff_files:
            c_files_match = c_files_regex.match(diff_file)
            if c_files_match:
                differing_c_files.append(diff_file)
        if len(differing_c_files) > 0:
            print "C files whose content differs ({dirname}): [{differing_c_files}]".format(dirname=dcmp.right, differing_c_files=', '.join(differing_c_files))
            return True
    for sub_dcmp in dcmp.subdirs.values():
        return diffs_found(sub_dcmp)
    return False

if __name__ == '__main__':
    main()

示例输出

(.virtualenvs)macbook:dircomparison joeyoung$ python ccodedircomparison.py 
Drivers deleted from /Users/joeyoung/web/Whosebug/dircomparison/test2/support: [thisismissingfromtest2.c]
Drivers added to /Users/joeyoung/web/Whosebug/dircomparison/test2/support: [addedfile1.h]
C files whose content differs (/Users/joeyoung/web/Whosebug/dircomparison/test2/support): [samefilenamedifftext1.h, samefilename1.c]
FLAG = 1

测试环境目录结构

(.virtualenvs)macbook:dircomparison joeyoung$ tree test1 test2
test1
├── affected.test.js
├── blob.test.js
├── cache.test.js
├── constants.test.js
├── database_fail.test.js
├── each.test.js
├── exec.test.js
├── extension.test.js
├── fts-content.test.js
├── issue-108.test.js
├── map.test.js
├── named_columns.test.js
├── named_params.test.js
├── null_error.test.js
├── nw
│   ├── Makefile
│   ├── index.html
│   ├── package.json
│   ├── thisismissingfromtest2.c
│   └── thisismissingfromtest2.txt
├── open_close.test.js
├── other_objects.test.js
├── parallel_insert.test.js
├── prepare.test.js
├── profile.test.js
├── rerun.test.js
├── scheduling.test.js
├── serialization.test.js
├── support
│   ├── createdb.js
│   ├── elmo.png
│   ├── helper.js
│   ├── onlyintest1.txt
│   ├── prepare.db
│   ├── samefilename1.c
│   ├── samefilename1.txt
│   ├── samefilenamedifftext1.h
│   ├── samefilenamedsametext1.h
│   ├── script.sql
│   ├── thisismissingfromtest2.c
│   └── thisismissingfromtest2.txt
├── trace.test.js
└── unicode.test.js
test2
├── affected.test.js
├── blob.test.js
├── cache.test.js
├── constants.test.js
├── database_fail.test.js
├── each.test.js
├── exec.test.js
├── extension.test.js
├── fts-content.test.js
├── issue-108.test.js
├── map.test.js
├── named_columns.test.js
├── named_params.test.js
├── null_error.test.js
├── nw
│   ├── Makefile
│   ├── index.html
│   └── package.json
├── open_close.test.js
├── other_objects.test.js
├── parallel_insert.test.js
├── prepare.test.js
├── profile.test.js
├── rerun.test.js
├── scheduling.test.js
├── serialization.test.js
├── support
│   ├── addedfile1.h
│   ├── createdb.js
│   ├── elmo.png
│   ├── helper.js
│   ├── prepare.db
│   ├── samefilename1.c
│   ├── samefilename1.txt
│   ├── samefilenamedifftext1.h
│   ├── samefilenamedsametext1.h
│   └── script.sql
├── trace.test.js
└── unicode.test.js

编辑前的原始答案如下

我的例子并没有完全按照你的描述做,但是这个例子和 filecmp.dircmp() documentation 之间应该足够了你开始了。

dircomparison.py

from filecmp import dircmp

def main():
    dcmp = dircmp("/Users/joeyoung/web/Whosebug/dircomparison/test1", "/Users/joeyoung/web/Whosebug/dircomparison/test2")
    if diffs_found(dcmp):
        print "DIFFS FOUND!"
    else:
        print "NO DIFFS FOUND"


def diffs_found(dcmp):
    if len(dcmp.left_only) > 0:
        print dcmp.report_full_closure()
        return True
    elif len(dcmp.right_only) > 0:
        print dcmp.report_full_closure()
        return True
    else:
        for sub_dcmp in dcmp.subdirs.values():
            if diffs_found(sub_dcmp):
                return True
    return False

if __name__ == '__main__':
    main()

示例输出

(.virtualenvs)macbook:dircomparison joeyoung$ python dircomparison.py 
diff /Users/joeyoung/web/Whosebug/dircomparison/test1/support /Users/joeyoung/web/Whosebug/dircomparison/test2/support
Only in /Users/joeyoung/web/Whosebug/dircomparison/test1/support : ['onlyintest1.txt']
Identical files : ['createdb.js', 'elmo.png', 'helper.js', 'prepare.db', 'script.sql']
None
DIFFS FOUND!

实际目录结构 这样你就可以看到我的测试环境是什么样子了。

(.virtualenvs)macbook:dircomparison joeyoung$ tree test1
test1
├── affected.test.js
├── blob.test.js
├── cache.test.js
├── constants.test.js
├── database_fail.test.js
├── each.test.js
├── exec.test.js
├── extension.test.js
├── fts-content.test.js
├── issue-108.test.js
├── map.test.js
├── named_columns.test.js
├── named_params.test.js
├── null_error.test.js
├── nw
│   ├── Makefile
│   ├── index.html
│   └── package.json
├── open_close.test.js
├── other_objects.test.js
├── parallel_insert.test.js
├── prepare.test.js
├── profile.test.js
├── rerun.test.js
├── scheduling.test.js
├── serialization.test.js
├── support
│   ├── createdb.js
│   ├── elmo.png
│   ├── helper.js
│   ├── onlyintest1.txt
│   ├── prepare.db
│   └── script.sql
├── trace.test.js
└── unicode.test.js

2 directories, 33 files
(.virtualenvs)macbook:dircomparison joeyoung$ tree test2
test2
├── affected.test.js
├── blob.test.js
├── cache.test.js
├── constants.test.js
├── database_fail.test.js
├── each.test.js
├── exec.test.js
├── extension.test.js
├── fts-content.test.js
├── issue-108.test.js
├── map.test.js
├── named_columns.test.js
├── named_params.test.js
├── null_error.test.js
├── nw
│   ├── Makefile
│   ├── index.html
│   └── package.json
├── open_close.test.js
├── other_objects.test.js
├── parallel_insert.test.js
├── prepare.test.js
├── profile.test.js
├── rerun.test.js
├── scheduling.test.js
├── serialization.test.js
├── support
│   ├── createdb.js
│   ├── elmo.png
│   ├── helper.js
│   ├── prepare.db
│   └── script.sql
├── trace.test.js
└── unicode.test.js

2 directories, 32 files