Ich habe eine Datei mit einem -|
Trennzeichen nach jedem Abschnitt ... muss mit Unix separate Dateien für jeden Abschnitt erstellen.
Beispiel einer Eingabedatei
wertretr
ewretrtret
1212132323
000232
-|
ereteertetet
232434234
erewesdfsfsfs
0234342343
-|
jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|
Erwartetes Ergebnis in Datei 1
wertretr
ewretrtret
1212132323
000232
-|
Erwartetes Ergebnis in Datei 2
ereteertetet
232434234
erewesdfsfsfs
0234342343
-|
Erwartetes Ergebnis in Datei 3
jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|
Antworten:
Ein Einzeiler, keine Programmierung. (außer dem regulären Ausdruck usw.)
csplit --digits=2 --quiet --prefix=outfile infile "/-|/+1" "{*}"
getestet am:
csplit (GNU coreutils) 8.30
Hinweise zur Verwendung auf Apple Mac
"Beachten Sie für OS X-Benutzer, dass die
csplit
mit dem Betriebssystem gelieferte Version nicht funktioniert. Sie möchten die Version in coreutils (über Homebrew installierbar), die aufgerufen wirdgcsplit
." - @Danial"Nur um hinzuzufügen, Sie können die Version für OS X zum Laufen bringen (zumindest mit High Sierra). Sie müssen nur die Argumente ein wenig optimieren
csplit -k -f=outfile infile "/-\|/+1" "{3}"
. Funktionen, die nicht zu funktionieren scheinen, sind die"{*}"
, auf die ich mich genau konzentrieren musste die Anzahl der Trennzeichen, die hinzugefügt werden müssen-k
, um zu vermeiden, dass alle Outfiles gelöscht werden, wenn kein endgültiges Trennzeichen gefunden wird. Auch wenn Sie möchten--digits
, müssen Sie-n
stattdessen verwenden. " - @Pebblquelle
--elide-empty-files
, sonst wird am Ende eine leere Datei angezeigt.--digits=2
Steuert die Anzahl der Ziffern, die zum Nummerieren der Ausgabedateien verwendet werden (2 ist für mich die Standardeinstellung, daher nicht erforderlich).--quiet
unterdrückt die Ausgabe (auch nicht wirklich notwendig oder hier gefragt).--prefix
Gibt das Präfix der Ausgabedateien an (Standard ist xx). So können Sie alle Parameter überspringen und erhalten Ausgabedateien wiexx12
.csplit -k -f=outfile infile "/-\|/+1" "{3}"
. Funktionen, die nicht zu funktionieren scheinen, sind"{*}"
: Ich musste die Anzahl der Trennzeichen-k
genau angeben und sie hinzufügen , um zu vermeiden, dass alle Outfiles gelöscht werden, wenn kein endgültiges Trennzeichen gefunden werden kann. Auch wenn Sie möchten--digits
, müssen Sie-n
stattdessen verwenden.awk '{f="file" NR; print $0 " -|"> f}' RS='-\\|' input-file
Erklärung (bearbeitet):
RS
ist das Datensatztrennzeichen, und diese Lösung verwendet eine gnu awk-Erweiterung, die es erlaubt, mehr als ein Zeichen zu sein.NR
ist die Datensatznummer.Die print-Anweisung druckt einen Datensatz gefolgt von
" -|"
einer Datei, deren Name die Datensatznummer enthält.quelle
RS
ist das Datensatztrennzeichen, und diese Lösung verwendet eine gnu awk-Erweiterung, die es erlaubt, mehr als ein Zeichen zu sein. NR ist die Datensatznummer. Die print-Anweisung druckt einen Datensatz gefolgt von "- |" in eine Datei, deren Name die Datensatznummer enthält.>
, sodass Sie ihn beliebig erstellen können. zBprint $0 "-|" > "file" NR ".txt"
awk '{f="file" NR; print $0 " -|" > f}'
Debian hat
csplit
, aber ich weiß nicht, ob das allen / den meisten / anderen Distributionen gemeinsam ist. Wenn nicht, sollte es nicht zu schwierig sein, die Quelle aufzuspüren und zu kompilieren ...quelle
csplit
es sich um POSIX handelt, würde ich erwarten, dass es auf im Wesentlichen allen Unix-ähnlichen Systemen verfügbar ist.csplit --prefix gold-data - "/^==*$/
vscsplit --prefix gold-data - "/^=+$/
. Zumindest hat GNU grep-e
.Ich habe ein etwas anderes Problem gelöst, bei dem die Datei eine Zeile mit dem Namen enthält, in die der folgende Text gehen soll. Dieser Perl-Code macht den Trick für mich:
#!/path/to/perl -w #comment the line below for UNIX systems use Win32::Clipboard; # Get command line flags #print ($#ARGV, "\n"); if($#ARGV == 0) { print STDERR "usage: ncsplit.pl --mff -- filename.txt [...] \n\nNote that no space is allowed between the '--' and the related parameter.\n\nThe mff is found on a line followed by a filename. All of the contents of filename.txt are written to that file until another mff is found.\n"; exit; } # this package sets the ARGV count variable to -1; use Getopt::Long; my $mff = ""; GetOptions('mff' => \$mff); # set a default $mff variable if ($mff eq "") {$mff = "-#-"}; print ("using file switch=", $mff, "\n\n"); while($_ = shift @ARGV) { if(-f "$_") { push @filelist, $_; } } # Could be more than one file name on the command line, # but this version throws away the subsequent ones. $readfile = $filelist[0]; open SOURCEFILE, "<$readfile" or die "File not found...\n\n"; #print SOURCEFILE; while (<SOURCEFILE>) { /^$mff (.*$)/o; $outname = $1; # print $outname; # print "right is: $1 \n"; if (/^$mff /) { open OUTFILE, ">$outname" ; print "opened $outname\n"; } else {print OUTFILE "$_"}; }
quelle
while
Schleife. Wenn dermff
reguläre Ausdruck am Zeilenanfang gefunden wird, wird der Rest der Zeile als Dateiname zum Öffnen und Schreiben verwendet. Es schließt nie etwas, so dass nach ein paar Dutzend keine Dateihandles mehr vorhanden sind.while
Schleife entfernt undwhile (<>)
Der folgende Befehl funktioniert für mich. Ich hoffe es hilft.
awk 'BEGIN{file = 0; filename = "output_" file ".txt"} /-|/ {getline; file ++; filename = "output_" file ".txt"} {print $0 > filename}' input
quelle
close
die alte Datei explizit zu verwenden, wenn Sie eine neue starten.if (file) close(filename);
bevor ein neuerfilename
Wert zugewiesen wird.; close(filename)
. Wirklich einfach, aber es behebt wirklich das obige BeispielSie können auch awk verwenden. Ich bin mit awk nicht sehr vertraut, aber das Folgende schien für mich zu funktionieren. Es wurden part1.txt, part2.txt, part3.txt und part4.txt generiert. Beachten Sie, dass die letzte partn.txt-Datei, die dadurch generiert wird, leer ist. Ich bin mir nicht sicher, wie ich das beheben soll, aber ich bin mir sicher, dass dies mit ein wenig Optimierung erreicht werden könnte. Irgendwelche Vorschläge jemand?
awk_pattern Datei:
BEGIN{ fn = "part1.txt"; n = 1 } { print > fn if (substr($0,1,2) == "-|") { close (fn) n++ fn = "part" n ".txt" } }
Bash-Befehl:
awk -f awk_pattern input.file
quelle
Hier ist ein Python 3-Skript, das eine Datei basierend auf einem von den Trennzeichen angegebenen Dateinamen in mehrere Dateien aufteilt. Beispiel für eine Eingabedatei:
# Ignored ######## FILTER BEGIN foo.conf This goes in foo.conf. ######## FILTER END # Ignored ######## FILTER BEGIN bar.conf This goes in bar.conf. ######## FILTER END
Hier ist das Skript:
#!/usr/bin/env python3 import os import argparse # global settings start_delimiter = '######## FILTER BEGIN' end_delimiter = '######## FILTER END' # parse command line arguments parser = argparse.ArgumentParser() parser.add_argument("-i", "--input-file", required=True, help="input filename") parser.add_argument("-o", "--output-dir", required=True, help="output directory") args = parser.parse_args() # read the input file with open(args.input_file, 'r') as input_file: input_data = input_file.read() # iterate through the input data by line input_lines = input_data.splitlines() while input_lines: # discard lines until the next start delimiter while input_lines and not input_lines[0].startswith(start_delimiter): input_lines.pop(0) # corner case: no delimiter found and no more lines left if not input_lines: break # extract the output filename from the start delimiter output_filename = input_lines.pop(0).replace(start_delimiter, "").strip() output_path = os.path.join(args.output_dir, output_filename) # open the output file print("extracting file: {0}".format(output_path)) with open(output_path, 'w') as output_file: # while we have lines left and they don't match the end delimiter while input_lines and not input_lines[0].startswith(end_delimiter): output_file.write("{0}\n".format(input_lines.pop(0))) # remove end delimiter if present if not input_lines: input_lines.pop(0)
Zum Schluss führen Sie Folgendes aus:
$ python3 script.py -i input-file.txt -o ./output-folder/
quelle
Verwenden
csplit
Sie, wenn Sie es haben.Wenn Sie dies nicht tun, aber Python haben, verwenden Sie Perl nicht.
Faules Lesen der Datei
Ihre Datei ist möglicherweise zu groß, um sie gleichzeitig im Speicher zu speichern. Das zeilenweise Lesen ist möglicherweise vorzuziehen. Angenommen, die Eingabedatei heißt "samplein":
$ python3 -c "from itertools import count with open('samplein') as file: for i in count(): firstline = next(file, None) if firstline is None: break with open(f'out{i}', 'w') as out: out.write(firstline) for line in file: out.write(line) if line == '-|\n': break"
quelle
cat file| ( I=0; echo -n "">file0; while read line; do echo $line >> file$I; if [ "$line" == '-|' ]; then I=$[I+1]; echo -n "" > file$I; fi; done )
und die formatierte Version:
#!/bin/bash cat FILE | ( I=0; echo -n"">file0; while read line; do echo $line >> file$I; if [ "$line" == '-|' ]; then I=$[I+1]; echo -n "" > file$I; fi; done; )
quelle
cat
nutzlos .cat
in jeder Situation eine einzelne Datei vermeiden können. Es gibt eine Stapelüberlauffrage mit mehr Diskussion (obwohl die akzeptierte Antwort IMHO aus ist); stackoverflow.com/questions/11710552/useless-use-of-catcsplit
, ist eine Awk-Lösung dieser Lösung wahrscheinlich viel vorzuziehen (selbst wenn Sie die von shellcheck.net usw. gemeldeten Probleme beheben würden ; beachten Sie, dass derzeit nicht alle darin enthaltenen Fehler gefunden werden).cat
immer noch nutzlos, und der Rest des Skripts könnte stark vereinfacht und korrigiert werden. aber es wird immer noch langsam sein. Siehe z. B. stackoverflow.com/questions/13762625/…Dies ist die Art von Problem, für das ich Context-Split geschrieben habe: http://stromberg.dnsalias.org/~strombrg/context-split.html
$ ./context-split -h usage: ./context-split [-s separator] [-n name] [-z length] -s specifies what regex should separate output files -n specifies how output files are named (default: numeric -z specifies how long numbered filenames (if any) should be -i include line containing separator in output files operations are always performed on stdin
quelle
csplit
. Siehe die Antwort von @ richard .Hier ist ein Perl-Code, der das Ding macht
#!/usr/bin/perl open(FI,"file.txt") or die "Input file not found"; $cur=0; open(FO,">res.$cur.txt") or die "Cannot open output file $cur"; while(<FI>) { print FO $_; if(/^-\|/) { close(FO); $cur++; open(FO,">res.$cur.txt") or die "Cannot open output file $cur" } } close(FO);
quelle