Автор оригинала: Doug Hellmann.
Цель:
Сравните файлы и каталоги в файловой системе.
Модуль filecmp
включает функции и класс для сравнения файлов и каталогов в файловой системе.
Пример данных
В примерах этого обсуждения используется набор тестовых файлов, созданных filecmp_mkexamples.py
.
filecmp_mkexamples.py
import os def mkfile(filename, bodyNone): with open(filename, 'w') as f: f.write(body or filename) return def make_example_dir(top): if not os.path.exists(top): os.mkdir(top) curdir os.getcwd() os.chdir(top) os.mkdir('dir1') os.mkdir('dir2') mkfile('dir1/file_only_in_dir1') mkfile('dir2/file_only_in_dir2') os.mkdir('dir1/dir_only_in_dir1') os.mkdir('dir2/dir_only_in_dir2') os.mkdir('dir1/common_dir') os.mkdir('dir2/common_dir') mkfile('dir1/common_file', 'this file is the same') os.link('dir1/common_file', 'dir2/common_file') mkfile('dir1/contents_differ') mkfile('dir2/contents_differ') # Update the access and modification times so most of the stat # results will match. st os.stat('dir1/contents_differ') os.utime('dir2/contents_differ', (st.st_atime, st.st_mtime)) mkfile('dir1/file_in_dir1', 'This is a file in dir1') os.mkdir('dir2/file_in_dir1') os.chdir(curdir) return if __name__ '__main__': os.chdir(os.path.dirname(__file__) or os.getcwd()) make_example_dir('example') make_example_dir('example/dir1/common_dir') make_example_dir('example/dir2/common_dir')
При запуске сценария создается дерево файлов в каталоге example
:
$ find example | sort example example/dir1 example/dir1/common_dir example/dir1/common_dir/dir1 example/dir1/common_dir/dir1/common_dir example/dir1/common_dir/dir1/common_file example/dir1/common_dir/dir1/contents_differ example/dir1/common_dir/dir1/dir_only_in_dir1 example/dir1/common_dir/dir1/file_in_dir1 example/dir1/common_dir/dir1/file_only_in_dir1 example/dir1/common_dir/dir2 example/dir1/common_dir/dir2/common_dir example/dir1/common_dir/dir2/common_file example/dir1/common_dir/dir2/contents_differ example/dir1/common_dir/dir2/dir_only_in_dir2 example/dir1/common_dir/dir2/file_in_dir1 example/dir1/common_dir/dir2/file_only_in_dir2 example/dir1/common_file example/dir1/contents_differ example/dir1/dir_only_in_dir1 example/dir1/file_in_dir1 example/dir1/file_only_in_dir1 example/dir2 example/dir2/common_dir example/dir2/common_dir/dir1 example/dir2/common_dir/dir1/common_dir example/dir2/common_dir/dir1/common_file example/dir2/common_dir/dir1/contents_differ example/dir2/common_dir/dir1/dir_only_in_dir1 example/dir2/common_dir/dir1/file_in_dir1 example/dir2/common_dir/dir1/file_only_in_dir1 example/dir2/common_dir/dir2 example/dir2/common_dir/dir2/common_dir example/dir2/common_dir/dir2/common_file example/dir2/common_dir/dir2/contents_differ example/dir2/common_dir/dir2/dir_only_in_dir2 example/dir2/common_dir/dir2/file_in_dir1 example/dir2/common_dir/dir2/file_only_in_dir2 example/dir2/common_file example/dir2/contents_differ example/dir2/dir_only_in_dir2 example/dir2/file_in_dir1 example/dir2/file_only_in_dir2
Та же структура каталогов повторяется один раз в каталогах « common_dir
», чтобы дать интересные варианты рекурсивного сравнения.
Сравнение файлов
cmp ()
сравнивает два файла в файловой системе.
filecmp_cmp.py
import filecmp print('common_file :', end' ') print(filecmp.cmp('example/dir1/common_file', 'example/dir2/common_file', shallowTrue), end' ') print(filecmp.cmp('example/dir1/common_file', 'example/dir2/common_file', shallowFalse)) print('contents_differ:', end' ') print(filecmp.cmp('example/dir1/contents_differ', 'example/dir2/contents_differ', shallowTrue), end' ') print(filecmp.cmp('example/dir1/contents_differ', 'example/dir2/contents_differ', shallowFalse)) print('identical :', end' ') print(filecmp.cmp('example/dir1/file_only_in_dir1', 'example/dir1/file_only_in_dir1', shallowTrue), end' ') print(filecmp.cmp('example/dir1/file_only_in_dir1', 'example/dir1/file_only_in_dir1', shallowFalse))
Аргумент shallow
сообщает cmp ()
, следует ли просматривать содержимое файла в дополнение к его метаданным. По умолчанию выполняется неглубокое сравнение с использованием информации, доступной из os.stat ()
. Если результаты статистики совпадают, файлы считаются одинаковыми. Поскольку вывод stat включает индексный дескриптор в Linux, отдельные файлы не считаются одинаковыми, даже если все их другие метаданные (размер, время создания и т. Д.) Совпадают. В этих случаях сравнивается содержимое файла. Если shallow
имеет значение False
, содержимое файла всегда сравнивается.
$ python3 filecmp_cmp.py common_file : True True contents_differ: False False identical : True True
Чтобы сравнить набор файлов в двух каталогах без рекурсии, используйте cmpfiles ()
. Аргументы – это имена каталогов и список файлов, которые нужно проверить в двух местах. Передаваемый список общих файлов должен содержать только имена файлов (каталоги всегда приводят к несоответствию), и файлы должны присутствовать в обоих местах. В следующем примере показан простой способ создания общего списка. При сравнении также используется флаг shallow
, как и в случае с cmp ()
.
filecmp_cmpfiles.py
import filecmp import os # Determine the items that exist in both directories d1_contents set(os.listdir('example/dir1')) d2_contents set(os.listdir('example/dir2')) common list(d1_contents & d2_contents) common_files [ f for f in common if os.path.isfile(os.path.join('example/dir1', f)) ] print('Common files:', common_files) # Compare the directories match, mismatch, errors filecmp.cmpfiles( 'example/dir1', 'example/dir2', common_files, ) print('Match :', match) print('Mismatch :', mismatch) print('Errors :', errors)
cmpfiles ()
возвращает три списка имен файлов, содержащих файлы, которые совпадают, файлы, которые не совпадают, и файлы, которые не могут быть сравнены (из-за проблем с разрешениями или по любой другой причине).
$ python3 filecmp_cmpfiles.py Common files: ['contents_differ', 'file_in_dir1', 'common_file'] Match : ['common_file'] Mismatch : ['contents_differ', 'file_in_dir1'] Errors : []
Сравнение каталогов
Описанные ранее функции подходят для относительно простых сравнений. Для рекурсивного сравнения больших деревьев каталогов или для более полного анализа более полезен класс dircmp
. В простейшем случае report ()
печатает отчет, в котором сравниваются два каталога.
filecmp_dircmp_report.py
import filecmp dc filecmp.dircmp('example/dir1', 'example/dir2') dc.report()
Результатом является отчет в виде простого текста, показывающий результаты только содержимого указанных каталогов без рекурсии.
$ python3 filecmp_dircmp_report.py diff example/dir1 example/dir2 Only in example/dir1 : ['dir_only_in_dir1', 'file_only_in_dir1'] Only in example/dir2 : ['dir_only_in_dir2', 'file_only_in_dir2'] Identical files : ['common_file'] Differing files : ['contents_differ'] Common subdirectories : ['common_dir'] Common funny cases : ['file_in_dir1']
Для получения дополнительных сведений и рекурсивного сравнения используйте report_full_closure ()
:
filecmp_dircmp_report_full_closure.py
import filecmp dc filecmp.dircmp('example/dir1', 'example/dir2') dc.report_full_closure()
Вывод включает сравнения всех параллельных подкаталогов.
$ python3 filecmp_dircmp_report_full_closure.py diff example/dir1 example/dir2 Only in example/dir1 : ['dir_only_in_dir1', 'file_only_in_dir1'] Only in example/dir2 : ['dir_only_in_dir2', 'file_only_in_dir2'] Identical files : ['common_file'] Differing files : ['contents_differ'] Common subdirectories : ['common_dir'] Common funny cases : ['file_in_dir1'] diff example/dir1/common_dir example/dir2/common_dir Common subdirectories : ['dir1', 'dir2'] diff example/dir1/common_dir/dir1 example/dir2/common_dir/dir1 Identical files : ['common_file', 'contents_differ', 'file_in_dir1', 'file_only_in_dir1'] Common subdirectories : ['common_dir', 'dir_only_in_dir1'] diff example/dir1/common_dir/dir1/common_dir example/dir2/common_dir/dir1/common_dir diff example/dir1/common_dir/dir1/dir_only_in_dir1 example/dir2/common_dir/dir1/dir_only_in_dir1 diff example/dir1/common_dir/dir2 example/dir2/common_dir/dir2 Identical files : ['common_file', 'contents_differ', 'file_only_in_dir2'] Common subdirectories : ['common_dir', 'dir_only_in_dir2', 'file_in_dir1'] diff example/dir1/common_dir/dir2/common_dir example/dir2/common_dir/dir2/common_dir diff example/dir1/common_dir/dir2/dir_only_in_dir2 example/dir2/common_dir/dir2/dir_only_in_dir2 diff example/dir1/common_dir/dir2/file_in_dir1 example/dir2/common_dir/dir2/file_in_dir1
Использование различий в программе
Помимо создания печатных отчетов, dircmp
вычисляет списки файлов, которые можно напрямую использовать в программах. Каждый из следующих атрибутов вычисляется только по запросу, поэтому создание экземпляра dircmp
не вызывает накладных расходов из-за неиспользуемых данных.
filecmp_dircmp_list.py
import filecmp import pprint dc filecmp.dircmp('example/dir1', 'example/dir2') print('Left:') pprint.pprint(dc.left_list) print('\nRight:') pprint.pprint(dc.right_list)
Файлы и подкаталоги, содержащиеся в сравниваемых каталогах, перечислены в left_list
и right_list
.
$ python3 filecmp_dircmp_list.py Left: ['common_dir', 'common_file', 'contents_differ', 'dir_only_in_dir1', 'file_in_dir1', 'file_only_in_dir1'] Right: ['common_dir', 'common_file', 'contents_differ', 'dir_only_in_dir2', 'file_in_dir1', 'file_only_in_dir2']
Входные данные можно фильтровать, передавая конструктору список имен, которые следует игнорировать. По умолчанию имена RCS
, CVS
и теги
игнорируются.
filecmp_dircmp_list_filter.py
import filecmp import pprint dc filecmp.dircmp('example/dir1', 'example/dir2', ignore['common_file']) print('Left:') pprint.pprint(dc.left_list) print('\nRight:') pprint.pprint(dc.right_list)
В этом случае « common_file
» не входит в список файлов для сравнения.
$ python3 filecmp_dircmp_list_filter.py Left: ['common_dir', 'contents_differ', 'dir_only_in_dir1', 'file_in_dir1', 'file_only_in_dir1'] Right: ['common_dir', 'contents_differ', 'dir_only_in_dir2', 'file_in_dir1', 'file_only_in_dir2']
Имена файлов, общие для обоих входных каталогов, сохраняются в common
, а файлы, уникальные для каждого каталога, перечислены в left_only
и right_only
.
filecmp_dircmp_membership.py
import filecmp import pprint dc filecmp.dircmp('example/dir1', 'example/dir2') print('Common:') pprint.pprint(dc.common) print('\nLeft:') pprint.pprint(dc.left_only) print('\nRight:') pprint.pprint(dc.right_only)
«Левый» каталог – это первый аргумент для dircmp ()
, а «правый» каталог – второй.
$ python3 filecmp_dircmp_membership.py Common: ['common_dir', 'common_file', 'contents_differ', 'file_in_dir1'] Left: ['dir_only_in_dir1', 'file_only_in_dir1'] Right: ['dir_only_in_dir2', 'file_only_in_dir2']
Общие члены могут быть далее разбиты на файлы, каталоги и «забавные» элементы (все, что имеет другой тип в двух каталогах или где есть ошибка от os.stat ()
).
filecmp_dircmp_common.py
import filecmp import pprint dc filecmp.dircmp('example/dir1', 'example/dir2') print('Common:') pprint.pprint(dc.common) print('\nDirectories:') pprint.pprint(dc.common_dirs) print('\nFiles:') pprint.pprint(dc.common_files) print('\nFunny:') pprint.pprint(dc.common_funny)
В данных примера элемент с именем « file_in_dir1
» представляет собой файл в одном каталоге и подкаталог в другом, поэтому он отображается в списке смешных.
$ python3 filecmp_dircmp_common.py Common: ['common_dir', 'common_file', 'contents_differ', 'file_in_dir1'] Directories: ['common_dir'] Files: ['common_file', 'contents_differ'] Funny: ['file_in_dir1']
Аналогичным образом разбираются различия между файлами.
filecmp_dircmp_diff.py
import filecmp dc filecmp.dircmp('example/dir1', 'example/dir2') print('Same :', dc.same_files) print('Different :', dc.diff_files) print('Funny :', dc.funny_files)
Файл not_the_same
сравнивается только с помощью os.stat ()
, а его содержимое не проверяется, поэтому он включается в список same_files
.
$ python3 filecmp_dircmp_diff.py Same : ['common_file'] Different : ['contents_differ'] Funny : []
Наконец, подкаталоги также сохраняются для упрощения рекурсивного сравнения.
filecmp_dircmp_subdirs.py
import filecmp dc filecmp.dircmp('example/dir1', 'example/dir2') print('Subdirectories:') print(dc.subdirs)
Атрибут subdirs
– это словарь, сопоставляющий имя каталога с новыми объектами dircmp
.
$ python3 filecmp_dircmp_subdirs.py Subdirectories: {'common_dir':}
Смотрите также
- стандартная библиотека документации для filecmp
- diffflib – вычисление различий между двумя последовательностями.