filecmp — Comparar archivos¶
Propósito: | Compara archivos y directorios en el sistema de archivos. |
---|
El módulo filecmp
incluye funciones y una clase para comparar archivos y
directorios en el sistema de archivos.
Datos de ejemplo¶
Los ejemplos en esta discusión usan un conjunto de archivos de prueba creados
por filecmp_mkexamples.py
.
import os
def mkfile(filename, body=None):
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')
mkfile('dir2/common_file', 'this file is the same')
mkfile('dir1/not_the_same')
mkfile('dir2/not_the_same')
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')
Ejecutar el script produce un árbol de archivos bajo el directorio example
:
$ find example
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/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/dir1/not_the_same
example/dir1/common_dir/dir2
example/dir1/common_dir/dir2/common_dir
example/dir1/common_dir/dir2/common_file
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_dir/dir2/not_the_same
example/dir1/common_file
example/dir1/dir_only_in_dir1
example/dir1/file_in_dir1
example/dir1/file_only_in_dir1
example/dir1/not_the_same
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/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/dir1/not_the_same
example/dir2/common_dir/dir2
example/dir2/common_dir/dir2/common_dir
example/dir2/common_dir/dir2/common_file
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_dir/dir2/not_the_same
example/dir2/common_file
example/dir2/dir_only_in_dir2
example/dir2/file_in_dir1
example/dir2/file_only_in_dir2
example/dir2/not_the_same
La misma estructura de directorio se repite una vez debajo de los directorios
«common_dir
para dar interesantes opciones de comparación recursiva.
Comparar archivos¶
cmp()
compara dos archivos en el sistema de archivos.
import filecmp
print('common_file :', end=' ')
print(filecmp.cmp('example/dir1/common_file',
'example/dir2/common_file'),
end=' ')
print(filecmp.cmp('example/dir1/common_file',
'example/dir2/common_file',
shallow=False))
print('not_the_same:', end=' ')
print(filecmp.cmp('example/dir1/not_the_same',
'example/dir2/not_the_same'),
end=' ')
print(filecmp.cmp('example/dir1/not_the_same',
'example/dir2/not_the_same',
shallow=False))
print('identical :', end=' ')
print(filecmp.cmp('example/dir1/file_only_in_dir1',
'example/dir1/file_only_in_dir1'),
end=' ')
print(filecmp.cmp('example/dir1/file_only_in_dir1',
'example/dir1/file_only_in_dir1',
shallow=False))
El argumento shallow
le dice a cmp()
si debe mirar los contenidos del
archivo, además de sus metadatos. El valor predeterminado es realizar una
comparación superficial utilizando la información disponible de os.stat()
.
Si los resultados de stat son los mismos, los archivos son considerado el mismo
para archivos del mismo tamaño creados al mismo tiempo. Se reportan como
iguales, incluso si sus contenidos difieren. Cuando shallow
es False
,
el contenido del archivo siempre se compara.
$ python3 filecmp_cmp.py
common_file : True True
not_the_same: True False
identical : True True
Para comparar un conjunto de archivos en dos directorios sin recurrir, usa
cmpfiles()
. Los argumentos son los nombres de los directorios y una lista
de archivos a verificar en las dos ubicaciones. La lista de archivos comunes
pasados deben contener solo nombres de archivos (los directorios siempre dan
como resultado que coinciden) y los archivos deben estar presentes en ambas
ubicaciones. El siguiente ejemplo muestra una forma sencilla de construir la
lista común. La comparación también toma el argumento shallow
, igual que
con cmp()
.
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()
devuelve tres listas de nombres de archivos que contienen
archivos que coinciden, los archivos que no coinciden y los archivos que no
pudieron ser comparados (debido a problemas de permiso o por cualquier otra
razón).
$ python3 filecmp_cmpfiles.py
Common files: ['not_the_same', 'file_in_dir1', 'common_file']
Match : ['not_the_same', 'common_file']
Mismatch : ['file_in_dir1']
Errors : []
Comparar directorios¶
Las funciones descritas anteriormente son adecuadas para comparaciones
relativamente simples. Para la comparación recursiva de grandes árboles de
directorios o para un análisis más completo, la clase dircmp
es más útil.
En su caso de uso más simple, report()
imprime un informe comparando dos
directorios
import filecmp
dc = filecmp.dircmp('example/dir1', 'example/dir2')
dc.report()
La salida es un informe de texto sin formato que muestra los resultados de solo
los contenidos de los directorios indicados, sin recursividad. En este caso,
el archivo «not_the_same
» se piensa que es el mismo porque los contenido no
estan siendo comparados. No hay manera de tener que dircmp
compare los
contenidos de archivos como lo hace cmp()
.
$ 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', 'not_the_same']
Common subdirectories : ['common_dir']
Common funny cases : ['file_in_dir1']
Para más detalles, y una comparación recursiva, usa report_full_closure()
:
import filecmp
dc = filecmp.dircmp('example/dir1', 'example/dir2')
dc.report_full_closure()
La salida incluye comparaciones de todos los subdirectorios paralelos.
$ 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', 'not_the_same']
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', 'file_in_dir1',
'file_only_in_dir1', 'not_the_same']
Common subdirectories : ['common_dir', 'dir_only_in_dir1']
diff example/dir1/common_dir/dir1/dir_only_in_dir1
example/dir2/common_dir/dir1/dir_only_in_dir1
diff example/dir1/common_dir/dir1/common_dir
example/dir2/common_dir/dir1/common_dir
diff example/dir1/common_dir/dir2 example/dir2/common_dir/dir2
Identical files : ['common_file', 'file_only_in_dir2',
'not_the_same']
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/file_in_dir1
example/dir2/common_dir/dir2/file_in_dir1
diff example/dir1/common_dir/dir2/dir_only_in_dir2
example/dir2/common_dir/dir2/dir_only_in_dir2
Usar las diferencias en un programa¶
Además de producir informes impresos, dircmp
calcula listas de archivos que
pueden ser utilizados directamente en programas. Cada uno de los siguientelos
atributos se calculan solo cuando se solicitan, por lo que crear una instancia
dircmp
no genera sobre carga por los datos no utilizados.
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)
Los archivos y subdirectorios contenidos en los directorios que están siendo
comparados se enumeran en left_list
y right_list
.
$ python3 filecmp_dircmp_list.py
Left:
['common_dir',
'common_file',
'dir_only_in_dir1',
'file_in_dir1',
'file_only_in_dir1',
'not_the_same']
Right:
['common_dir',
'common_file',
'dir_only_in_dir2',
'file_in_dir1',
'file_only_in_dir2',
'not_the_same']
Las entradas se pueden filtrar pasando una lista de nombres para ignorar al
constructor. Por defecto, los nombres RCS
, CVS
y tags
son
ignorados.
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)
En este caso, el «common_file
» se deja fuera de la lista de archivos a ser
comparados.
$ python3 filecmp_dircmp_list_filter.py
Left:
['common_dir',
'dir_only_in_dir1',
'file_in_dir1',
'file_only_in_dir1',
'not_the_same']
Right:
['common_dir',
'dir_only_in_dir2',
'file_in_dir1',
'file_only_in_dir2',
'not_the_same']
Los nombres de los archivos comunes a ambos directorios de entrada se guardan
en common
, y los archivos exclusivos de cada directorio se enumeran en
left_only
, y right_only
.
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)
El directorio «izquierdo» es el primer argumento de dircmp()
y el
directorio «derecho» es el segundo.
$ python3 filecmp_dircmp_membership.py
Common:
['file_in_dir1', 'common_file', 'common_dir', 'not_the_same']
Left:
['dir_only_in_dir1', 'file_only_in_dir1']
Right:
['file_only_in_dir2', 'dir_only_in_dir2']
Los miembros comunes se pueden dividir en archivos, directorios y artículos
«divertidos» (cualquier cosa que tenga un tipo diferente en los dos directorios
o donde hay un error de os.stat()
).
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)
En los datos de ejemplo, el elemento denominado «file_in_dir1
» es un
archivo en un directorio y un subdirectorio en el otro, por lo que se muestra
en la lista divertida
$ python3 filecmp_dircmp_common.py
Common:
['file_in_dir1', 'common_file', 'common_dir', 'not_the_same']
Directories:
['common_dir']
Files:
['common_file', 'not_the_same']
Funny:
['file_in_dir1']
Las diferencias entre archivos se desglosan de manera similar.
import filecmp
dc = filecmp.dircmp('example/dir1', 'example/dir2')
print('Same :', dc.same_files)
print('Different :', dc.diff_files)
print('Funny :', dc.funny_files)
El archivo not_the_same
solo se compara a través de os.stat()
, y los
contenidos no son examinados, por lo que está incluido en la lista
same_files
.
$ python3 filecmp_dircmp_diff.py
Same : ['common_file', 'not_the_same']
Different : []
Funny : []
Finalmente, los subdirectorios también se guardan para permitir comparación recursiva fácil.
import filecmp
dc = filecmp.dircmp('example/dir1', 'example/dir2')
print('Subdirectories:')
print(dc.subdirs)
El atributo subdirs
es un diccionario que mapea el nombre de directorio
a los nuevos objetos dircmp
.
$ python3 filecmp_dircmp_subdirs.py
Subdirectories:
{'common_dir': <filecmp.dircmp object at 0x1019b2be0>}
Ver también
- Standard library documentation for filecmp
difflib
– Comparar Secuencias