BookinglyTech News
Software

Por qué concatenar varios .tar.gz con cat no funciona y cómo combinarlos bien

tar cierra cada archivo con un marcador de fin que hace que el lector ignore lo que venga después; gzip no tiene ese marcador. Por eso el truco de cat sirve para unos formatos y falla en otros.

3 min de lecturaLobsters0 vistas

Juntar dos .tar.gz con cat no funciona. El resultado tiene pinta de archivo válido, pero cualquier lectura se detiene en el primer marcador de fin de fichero que encuentra y descarta todo lo que va detrás. La causa está en cómo están construidos por dentro los dos formatos: uno se diseñó sobre las limitaciones de la cinta magnética y el otro como respuesta a un problema de patentes.

tar viene de tape archive y su estructura arrastra las restricciones del soporte para el que nació. Las cintas se leen en secuencia, de principio a fin; solo se podía añadir al final de un registro, nunca reescribir bloques intermedios; y la capacidad y el tamaño de bloque estaban fijados. Un archivo tar es una sucesión de ficheros troceados en bloques de tamaño fijo, con un bloque de cabecera (nombre, tamaño) y bloques de datos. Después del último fichero vienen dos o más bloques llenos de ceros: el marcador de EOF.

De ahí salen varios comportamientos que despistan. El tamaño del fichero se declara en la cabecera antes de escribir los datos, así que si usas TarFile.addfile y olvidas fijar tarinfo.size, Python escribe un cero y genera un archivo vacío. Un tar puede llevar nombres repetidos: como no se puede borrar un bloque de la cinta, se actualiza un fichero añadiendo al final una versión nueva con el mismo nombre, y al desempaquetar la última pisa a la anterior. Y todo lo que quede tras el marcador de ceros se ignora, salvo que le pases a GNU tar el flag --ignore-zeros.

gzip sí se puede concatenar

gzip es un compresor de flujo: coge un único fichero o stream y lo hace más pequeño sin pérdida. Nació como reemplazo libre de compress, cuyo algoritmo LZW estaba protegido por patentes en aquella época. Sus tres restricciones de diseño salen de ahí: nada de patentes, memoria acotada para poder comprimir y descomprimir por trozos, y portabilidad total respecto a CPU, sistema operativo y sistema de ficheros. La RFC 1952 recoge ese formato.

Por dentro, un gzip es una secuencia de miembros, cada uno con su cabecera (nombre original, fecha de modificación), los datos comprimidos y un trailer con el CRC32 y el tamaño sin comprimir. No hay marcador de EOF. Los miembros no son ficheros individuales a efectos prácticos: se leen como un único flujo y no se pueden listar ni extraer por separado. Como van uno detrás de otro y no hay cierre, concatenar varios .gz con cat sí produce un archivo descomprimible de una pieza.

Cómo se combinan de verdad

La vía limpia es deshacer y volver a empaquetar con el módulo tarfile de Python: abres cada archivo de entrada, recorres sus miembros y los copias a un archivo de salida nuevo, que quedará con un único marcador de EOF y se leerá con la configuración por defecto.

import tarfile

def combine_tars(output_file, input_files):
    with tarfile.open(output_file, "w") as out:
        for f in input_files:
            with tarfile.open(f, "r") as src:
                for member in src.getmembers():
                    out.addfile(member, src.extractfile(member))

combine_tars("numbers.tar", ["one.tar", "two.tar", "three.tar"])

Es más código que pegar bytes en crudo, y el paso intermedio obliga a leer los datos, pero el resultado no exige flags especiales ni depende del lector. Si el pipeline genera varios .tar.gz y hay que unificarlos, ese es el punto donde conviene decidir: el atajo de cat parece inocente y produce archivos que solo un lector permisivo sabrá interpretar.