Cómo fusionar todos los archivos de otra carpeta en un solo archivo y agregar una nueva línea

Cómo fusionar todos los archivos de otra carpeta en un solo archivo y agregar una nueva línea

Tengo muchas carpetas y archivos, esta es la estructura que tengo

26-09-2016/CHANGELOG_20160926.TXT
26-09-2016/FILE_CHANGELOG_20160926.TXT
27-09-2016/CHANGELOG_20160927.TXT
27-09-2016/FILE_CHANGELOG_20160927.TXT

Necesito el resultado de la siguiente manera. Todos los archivos con nombres similares CHANGELOG_*.TXTse fusionarán y agregarán una nueva línea separada en un archivo similar CHANGELOG_20160926-20160930.TXT, y todos los archivos con nombres FILE_CHANGELOG_*.TXTse fusionarán y agregarán una nueva línea separada en un archivo similar FILE_CHANGELOG_20160926-20160930.TXT.

¿Cómo puedo hacer eso?

Respuesta1

Dado que no especificó ningún requisito de idioma, existe la posibilidad de utilizar Python 3.

#/usr/bin/env python3

from glob import glob
from os.path import basename
import re

for prefix in ('CHANGELOG', 'FILE_CHANGELOG'):
    files = dict((int(re.split('[_.]', basename(f))[-2]), f)
                 for f in glob('*-*-*/%s_*.TXT' % prefix))
    out_file = '%s_%d-%d.TXT' % (prefix, min(files.keys()), max(files.keys()))

    with open(out_file, 'w') as f_out:
        for date in sorted(files.keys()):
            with open(files[date]) as f_in:
                for line in f_in:
                    f_out.write(line)
            f_out.write("\n")

Básicamente utiliza globy basenamepara enumerar y analizar nombres de archivos, clasificándolos por fecha. Los valores mínimo/máximo se utilizan para crear el nombre del archivo de salida, y todos los archivos se escriben allí en orden. No olvide ajustar los patrones a su estructura de directorio real, si fuera necesario. Luego, simplemente chmody ejecuta:

$ chmod +x script.py
$ ./script.py

Respuesta2

Solución enTXR:

Primero, tratemos esto como una tarea de procesamiento de texto, asumiendo que tenemos la lista de ejemplo de nombres de rutas en un archivo de entrada llamado paths. Transformamos pathsen comandos de shell que catagrupan archivos y producen los archivos de salida requeridos:

@(do
   (defstruct file-info nil
     full-name
     root-name
     date-key
     (:method equal (self) self.date-key)))
@(collect :vars (files))
@  (all)
@dd-@mm-@yyyy/@*{name}_@yyyy@[email protected]
@  (and)
@path
@  (end)
@  (bind files @(new file-info full-name path root-name name
                     date-key ^(,yyyy ,mm ,dd)))
@(end)
@(do
   (let ((h (group-by (usl root-name) files :equal-based)))
     [hash-update h sort]
     (dohash (name flist h)
       (let ((start (find-min flist))
             (end (find-max flist))
             (paths (mapcar (usl full-name) flist)))
         (put-line `cat @{paths " "} >\ \
                    @{start.root-name}_@{start.date-key ""}- \
                    @{end.date-key ""}.TXT`)))))

Correr:

$ txr catfiles.txr paths
cat 26-09-2016/CHANGELOG_20160926.TXT 27-09-2016/CHANGELOG_20160927.TXT > CHANGELOG_20160926-20160927.TXT
cat 26-09-2016/FILE_CHANGELOG_20160926.TXT 27-09-2016/FILE_CHANGELOG_20160927.TXT > FILE_CHANGELOG_20160926-20160927.TXT

Para trabajar en rutas reales y ejecutar los catcomandos se requieren modificaciones simples:

@(do
   (defstruct file-info nil
     full-name
     root-name
     date-key
     (:method equal (self) self.date-key)))
@(next :list (glob "*/*.TXT"))
@(collect :vars (files))
@  (all)
@dd-@mm-@yyyy/@*{name}_@yyyy@[email protected]
@  (and)
@path
@  (end)
@  (bind files @(new file-info full-name path root-name name
                     date-key ^(,yyyy ,mm ,dd)))
@(end)
@(do
   (let ((h (group-by (usl root-name) files :equal-based)))
     [hash-update h sort]
     (dohash (name flist h)
       (let ((start (find-min flist))
             (end (find-max flist))
             (paths (mapcar (usl full-name) flist)))
         (sh `cat @{paths " "} >\ \
              @{start.root-name}_@{start.date-key ""}- \
              @{end.date-key ""}.TXT`)))))

Los únicos cambios son la adición de a @(next :list (glob "*/*.TXT"))para redirigir el escaneo de entrada a través de una lista de rutas obtenidas del sistema de archivos y un cambio de put-stringa shpara catejecutar los comandos.

Si las listas de archivos pueden ser muy grandes, nos encontraremos con los límites de paso de comandos/argv del sistema operativo: no podemos capturarlos en un solo comando.

Una posible solución para esto es cambiar la última parte del código a:

@(do
   (let ((h (group-by (usl root-name) files :equal-based)))
     (hash-update h (op sort))
     (dohash (name flist h)
       (let* ((start (find-min flist))
              (end (find-max flist))
              (paths (mapcar (usl full-name) flist))
              (target `@{start.root-name}_@{start.date-key ""}- \
                       @{end.date-key ""}.TXT`))
         (sh `> @target`)
         (each ((group-of-ten (tuples 10 paths)))
           (sh `cat @{group-of-ten " "} >> @target`))))))

Es decir, para cada archivo, utilícelo > filepara asegurarse de que exista y esté truncado a cero. Luego utilícelo cat ... >> filepara agregarle los registros, en grupos de diez.

información relacionada