Como mesclar todos os arquivos de outra pasta em um arquivo e adicionar nova linha

Como mesclar todos os arquivos de outra pasta em um arquivo e adicionar nova linha

Tenho muitas pastas e arquivos, essa é a estrutura que tenho

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

Eu preciso da saída da seguinte maneira. Todos os arquivos com nome semelhante CHANGELOG_*.TXTdevem ser mesclados e adicionar uma nova linha separada em um arquivo como CHANGELOG_20160926-20160930.TXT, e todos os arquivos com nome FILE_CHANGELOG_*.TXTdevem ser mesclados e adicionar uma nova linha separada em um arquivo também como FILE_CHANGELOG_20160926-20160930.TXT.

Como eu posso fazer isso?

Responder1

Como você não especificou nenhum requisito de linguagem, aqui está uma possibilidade de usar 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")

Basicamente usa globe basenamepara listar e analisar nomes de arquivos, classificando-os por data. Os valores mínimo/máximo são usados ​​para construir o nome do arquivo de saída e todos os arquivos são gravados lá em ordem. Não se esqueça de ajustar os padrões à sua estrutura de diretórios real, caso seja necessário. Então é só chmodexecutar:

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

Responder2

Solução emTXR:

Primeiro, vamos tratar isso como uma tarefa de processamento de texto, assumindo que temos um exemplo de lista de nomes de caminhos em um arquivo de entrada chamado paths. Transformamos pathsem comandos shell que catagrupamos os arquivos e produzimos os arquivos de saída necessários:

@(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 trabalhar em caminhos reais e executar os catcomandos são necessárias modificações 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`)))))

As únicas mudanças são a adição de um @(next :list (glob "*/*.TXT"))para redirecionar a varredura de entrada sobre uma lista de caminhos agrupados no sistema de arquivos e uma mudança de put-stringpara shpara que os catcomandos sejam executados.

Se as listas de arquivos puderem ser muito grandes, encontraremos limites de passagem de comando/argv do sistema operacional: não podemos catá-los em um único comando.

Uma possível solução para isso é alterar a última parte do código para:

@(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`))))))

Ou seja, para cada arquivo, use > filepara garantir que ele exista e seja truncado para zero. Em seguida, use cat ... >> filepara anexar os logs a ele, em grupos de dez.

informação relacionada