So führen Sie alle Dateien in anderen Ordnern zu einer Datei zusammen und fügen eine neue Zeile hinzu

So führen Sie alle Dateien in anderen Ordnern zu einer Datei zusammen und fügen eine neue Zeile hinzu

Ich habe viele Ordner und Dateien, das ist die Struktur, die ich habe

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

Ich brauche die folgende Ausgabe. Alle Dateien mit Namen wie CHANGELOG_*.TXTsollen zusammengeführt und mit einer separaten neuen Zeile in eine Datei wie eingefügt werden CHANGELOG_20160926-20160930.TXT, und alle Dateien mit Namen FILE_CHANGELOG_*.TXTsollen zusammengeführt und mit einer separaten neuen Zeile in eine Datei wie eingefügt werden FILE_CHANGELOG_20160926-20160930.TXT.

Wie kann ich das machen?

Antwort1

Da Sie keine Sprachanforderungen angegeben haben, hier eine Möglichkeit mit 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")

Es verwendet im Wesentlichen globund basenamezum Auflisten und Parsen von Dateinamen und sortiert sie nach Datum. Die Min-/Max-Werte werden zum Erstellen des Ausgabedateinamens verwendet und alle Dateien werden der Reihe nach dort geschrieben. Vergessen Sie nicht, die Muster an Ihre tatsächliche Verzeichnisstruktur anzupassen, falls dies erforderlich ist. Führen Sie dann einfach chmodund aus:

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

Antwort2

Lösung inTXR:

Behandeln wir dies zunächst als Textverarbeitungsaufgabe und gehen davon aus, dass wir eine Beispielliste mit Pfadnamen für eine Eingabedatei namens haben paths. Wir wandeln diese Dateigruppen pathsin Shell-Befehle um und erzeugen die erforderlichen Ausgabedateien:cat

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

Laufen:

$ 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

Um auf realen Pfaden zu arbeiten und die catBefehle auszuführen, sind einfache Änderungen erforderlich:

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

Die einzigen Änderungen sind die Hinzufügung von , @(next :list (glob "*/*.TXT"))um den Eingabescan über eine Liste von Pfaden umzuleiten, die aus dem Dateisystem globuliert wurden, und ein Wechsel von put-stringzu sh, um die catBefehle ausführen zu lassen.

Wenn die Dateilisten sehr groß sein können, stoßen wir auf die Übergabegrenzen von OS-Befehlen/Argvs: Wir können sie nicht in einem einzigen Befehl caten.

Eine mögliche Lösung hierfür besteht darin, den letzten Teil des Codes wie folgt zu ändern:

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

Stellen Sie also für jede Datei > filesicher, dass sie existiert und auf Null gekürzt wird. Fügen Sie dann cat ... >> filedie Protokolle in Zehnergruppen daran an.

verwandte Informationen