使用 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如下
在文件夹 2
中打印新添加的 driver 文件(仅 c 和 .h)
与文件夹 1 相比,打印删除了文件夹 2 中的 driver 个文件
(这可以通过两个杠杆来完成,用于循环存储 os.walk 结果在两个列表中,然后将它们相减)
- 我的挑战: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
以在 walk
ing 时轻松找到文件扩展名。
您应该能够使用 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
我正在尝试在 python 中编写一个函数来比较两个文件夹(具有完全相同的子目录结构和文件列表)。
该文件夹必然包含.c .h .txt .cat .sys .pdb,但主要集中在C 和header 文件上。
这个 diff(folder1, folder2) 的输出应该return如下
在文件夹 2
中打印新添加的 driver 文件(仅 c 和 .h)
与文件夹 1 相比,打印删除了文件夹 2 中的 driver 个文件
(这可以通过两个杠杆来完成,用于循环存储 os.walk 结果在两个列表中,然后将它们相减)
- 我的挑战: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
以在walk
ing 时轻松找到文件扩展名。
您应该能够使用 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