Сравнивать каталоги только по именам файлов / папок, печатая какие-либо различия?

Как рекурсивно сравнить два каталога (сравнение должно основываться только на имени файла) и распечатать файлы / папки только в одном или другом каталоге?

Я использую 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
Другие вопросы по тегам