Сравнивать каталоги только по именам файлов / папок, печатая какие-либо различия?
Как рекурсивно сравнить два каталога (сравнение должно основываться только на имени файла) и распечатать файлы / папки только в одном или другом каталоге?
Я использую Python 3.3.
Я видел filecmp
модуль, однако, кажется, не совсем то, что мне нужно. Самое главное, он сравнивает файлы, основываясь не только на имени файла.
Вот что у меня так далеко:
import filecmp
dcmp = filecmp.dircmp('./dir1', './dir2')
dcmp.report_full_closure()
dir1
выглядит так:
dir1
- atextfile.txt
- anotherfile.xml
- afolder
- testscript.py
- anotherfolder
- file.txt
- athirdfolder
А также dir2
выглядит так:
dir2
- atextfile.txt
- afolder
- testscript.py
- anotherfolder
- file.txt
- file2.txt
Я хочу, чтобы результаты выглядели примерно так:
files/folders only in dir1
* anotherfile.xml
* athirdfolder
files/folders only in dir2
* anotherfolder/file2.txt
Мне нужен простой питонический способ сравнить две директории, основанные только на имени файла / папки, и распечатать различия.
Также мне нужен способ проверить, идентичны ли каталоги или нет.
Примечание: я искал в stackru и Google что-то вроде этого. Я вижу много примеров того, как сравнивать файлы с учетом их содержимого, но я не могу найти ничего, кроме имен файлов.
3 ответа
Мое решение использует тип set () для хранения относительных путей. Тогда сравнение - это только вопрос вычитания множества.
import os
import re
def build_files_set(rootdir):
root_to_subtract = re.compile(r'^.*?' + rootdir + r'[\\/]{0,1}')
files_set = set()
for (dirpath, dirnames, filenames) in os.walk(rootdir):
for filename in filenames + dirnames:
full_path = os.path.join(dirpath, filename)
relative_path = root_to_subtract.sub('', full_path, count=1)
files_set.add(relative_path)
return files_set
def compare_directories(dir1, dir2):
files_set1 = build_files_set(dir1)
files_set2 = build_files_set(dir2)
return (files_set1 - files_set2, files_set2 - files_set1)
if __name__ == '__main__':
dir1 = 'old'
dir2 = 'new'
in_dir1, in_dir2 = compare_directories(dir1, dir2)
print '\nFiles only in {}:'.format(dir1)
for relative_path in in_dir1:
print '* {0}'.format(relative_path)
print '\nFiles only in {}:'.format(dir2)
for relative_path in in_dir2:
print '* {0}'.format(relative_path)
обсуждение
Рабочая лошадка - функция build_files_set(). Он пересекает каталог и создает набор относительных имен файлов / каталогов
Функция compare_directories() берет два набора файлов и возвращает различия - очень просто.
Основная идея, использовать метод os.walk, чтобы заполнить словари имен файлов, а затем сравнить словари.
import os
from os.path import join
fpa = {}
for root, dirs, files in os.walk('/your/path'):
for name in files:
fpa[name] = 1
fpb = {}
for root, dirs, files in os.walk('/your/path2'):
for name in files:
fpb[name] = 1
print "files only in a"
for name in fpa.keys():
if not(name in fpb.keys()):
print name,"\n"
print "files only in b"
for name in fpb.keys():
if not(name in fpa.keys()):
print name,"\n"
Я не проверял это, поэтому вам, возможно, придется исправить. Кроме того, его можно легко изменить, чтобы избежать повторного использования.
На самом деле, filecmp
Можно и нужно использовать для этого, но вы должны сделать немного кодирования.
- Вы даете
filecmp.dircmp()
две директории, которые он называет левой и правой. filecmp.dircmp.left_only
это список файлов и каталогов, которые находятся только в левом каталоге.filecmp.dircmp.right_only
это список файлов и каталогов, которые находятся только в нужном каталоге.filecmp.dircmp.common_dirs
это список папок, которые есть в обоих.
Вы можете использовать их для создания простой рекурсивной функции для поиска всех файлов и каталогов, которые не являются общими для обоих деревьев.
Код:
from os.path import join
from filecmp import dircmp
def find_uncommon(L_dir, R_dir):
dcmp = dircmp(L_dir, R_dir)
L_only = [join(L_dir, f) for f in dcmp.left_only]
R_only = [join(R_dir, f) for f in dcmp.right_only]
for sub_dir in dcmp.common_dirs:
new_L, new_R = find_uncommon(join(L_dir, sub_dir), join(R_dir, sub_dir))
L_only.extend(new_L)
R_only.extend(new_R)
return L_only, R_only
Прецедент:
C:/
L_dir/
file_in_both_trees.txt
file_in_L_tree.txt
dir_in_L_tree/
dir_in_both_trees/
file_in_both_trees.txt
file_in_L_tree.txt
dir_in_L_tree/
file_inside_dir_only_in_L_tree.txt
R_dir/
file_in_both_trees.txt
file_in_R_tree.txt
dir_in_R_tree/
dir_in_both_trees/
file_in_both_trees.txt
file_in_R_tree.txt
dir_in_R_tree/
file_inside_dir_only_in_R_tree.txt
Демо - версия:
L_only, R_only = find_uncommon('C:\\L_dir', 'C:\\R_dir')
print('Left only:\n\t' + '\n\t'.join(L_only))
print('Right only:\n\t' + '\n\t'.join(R_only))
Результат:
Left_only:
C:\L_dir\file_in_L_tree.txt
C:\L_dir\dir_in_L_tree
C:\L_dir\dir_in_both_trees\file_in_L_tree.txt
C:\L_dir\dir_in_both_trees\dir_in_L_tree
Right_only:
C:\R_dir\file_in_R_tree.txt
C:\L_dir\dir_in_R_tree
C:\R_dir\dir_in_both_trees\file_in_R_tree.txt
C:\R_dir\dir_in_both_trees\dir_in_R_tree
Обратите внимание, что вам придется немного изменить вышеприведенный код, если вы хотите видеть внутри необычных каталогов. То, о чем я говорю, - это два файла в моем примере выше:
file_inside_dir_only_in_L_tree.txt
file_inside_dir_only_in_R_tree.txt
Python 2:
import os
folder1 = os.listdir('/path1')
folder2 = os.listdir('/path2')
folder_diff = set(folder1) - set(folder2) if folder1 > folder2 else set(folder2) - set(folder1)
print folder_diff