2017-10-13 1 views
0

ich eine Datei haben, die unten Mustergeteilt eine Datei basierend auf einem Muster

HDR1|20160101|1234| 
N1|ABC| 
XXX|21431415|3522352352|ITEM| 
FORE|20140508|20140214| 
SD|0|0039 - data|data|data|data| 
SD|0|0211 - data|data|data|data| 
SD|0|0039 - data|data|data|data| 
SD|0|0211 - data|data|data|data| 
FORE|20140508|20140214| 
SD|0|0039 - data|data|data|data| 
SD|0|0039 - data|data|data|data| 
SD|0|0211 - data|data|data|data| 

möchte ich teilen Sie die Datei auf eine Größe basierend haben würde, aber auch um die unten nehmen müssen.

Die ersten 3 Zeilen ist die Kopfzeile, die ich in jede von mir erstellte Split-Datei aufnehmen muss. Die Zeile, die mit FORE beginnt, hat ihre unteren Zeilen, beginnend mit SD, also muss ich sie alle zusammenhalten.

Die Ausgabe sollte wie folgt aussehen.

Split File 1:

HDR1|20160101|1234| 
N1|ABC| 
XXX|21431415|3522352352|ITEM| 
FORE|20140508|20140214| 
SD|0|0039 - data|data|data|data| 
SD|0|0211 - data|data|data|data| 
SD|0|0039 - data|data|data|data| 
SD|0|0211 - data|data|data|data| 

Split File 2:

HDR1|20160101|1234| 
N1|ABC| 
XXX|21431415|3522352352|ITEM| 
FORE|20140508|20140214| 
SD|0|0039 - data|data|data|data| 
SD|0|0039 - data|data|data|data| 
SD|0|0211 - data|data|data|data| 

ich einen Pseudo-Code aufgebaut haben, die wie below.There aussieht, kann mehrere Sätze solcher FORE und SD sein, die ich Ich habe eine Schleife gelegt

create $file 
create $line_num=5 
create $file_size 
create $top_size=20mb 
read the first 4 lines of the original file and copy it in a temphdr file 
    Loop until last $line_num is encountered 
     read the header details and Append the header from the temphdr to the $file 
     for each $record starting the head -$line_num (5,6,7...etc) that contains FORE| in the first part 
      if the $file size is < $top_size 
       append the $record in the $file 

       increment $line_num 
       For each $record in head -$line_num that contains SD| in the first part 
        append the $record in the $file 
        increment $line_num 
      else 
       create a $file=$file+1 
      fi 
     end loop 
    end loop  

Könnte jemand mich wissen lassen, wenn es irgendeinen anderen Effekt gibt Eine andere Möglichkeit, awk und sed usw. zu verwenden, um dies anders als die oben erwähnte Logik auf hoher Ebene zu implementieren.

+0

Ich bemerke Ihre letzte Bearbeitung - haben Sie einen Grund zu glauben, dass meine Antwort * nicht das tut, wonach Sie fragen (re: Split nur bei einer FORE, behalten Sie es als Set mit den folgenden SDs)? –

+0

Hey Charles. Das tut mir leid. Irgendwie habe ich es anfangs gar nicht bemerkt, also habe ich meine Frage bearbeitet und gedacht, dass ich vielleicht nicht ausgearbeitet habe, aber dann habe ich sofort bemerkt, dass du meinen Punkt bereits berücksichtigt hast. :). Ich danke Ihnen dafür. – user3055262

+0

NP. Der einzige Ort, an dem es eine Frage der Interpretation gibt, ist, ob Sie sich bei * jedem * FORE aufteilen wollen (wie @ anubhavas Antwort tut), oder bei der ersten FORE nach einer 20-MB-Grenze (wie meine Antwort tut). –

Antwort

1

Nichts ist fast so komplex. Dies kann in einer reinen Shell ohne externe Befehle implementiert werden (keine head, awk, usw.).

#!/usr/bin/env ksh 

max_size=$((20 * 1024 * 1024)) 

# Read our three fixed header lines 
headers='' 
read -r line; headers+="$line"$'\n' 
read -r line; headers+="$line"$'\n' 
read -r line; headers+="$line"$'\n' 

splitNum=1            # variable to track file number 
splitFileName=$(printf 'split.%04d' "$splitNum")  # generate first filename 
exec >"$splitFileName"         # and redirect stdout to that file 

printf '%s' "${headers}"        # print our headers... 
cur_size=$((${#headers}))       # and set cur_size to their length 

while IFS= read -r line; do       # For each line: 
    # check for and manage rotation 
    if [[ $line = "FORE|"* ]]; then      # If it's a FORE... 
    if ((cur_size > max_size)); then     # ...and over size: start a new file 
     ((++splitNum))         # increment the split number 
     splitFileName=$(printf 'split.%04d' "$splitNum") # generate a new filename 
     exec >"$splitFileName"       # redirect stdout to that file 
     printf '%s' "${headers}"       # print headers to stdout 
     cur_size=$((${#headers}))      # reset size to size of headers 
    fi 
    fi 
    # whether or not we had to do any of that: 
    printf '%s\n' "$line"        # print the line we just read 
    cur_size=$((cur_size + ${#line} + 1))    # and increment cur_size 
done 

Beachten Sie, wenn Sie dies bash Portierung wurden, könnten Sie splitFileName=$(printf 'split.%04d' "$splitNum")-printf -v splitFileName 'split.%04d' "$splitNum" ändern möchten. ksh93 ist schlau genug, die an der Befehlssubstitution beteiligte Subshell automatisch zu optimieren; bash benötigt eine explizite Syntax, um den Overhead zu vermeiden.

1

können Sie diesen awk Befehl verwenden:

awk -F '|' 'NR<=3{ 
    hdr = hdr $0 RS 
} 
$1=="FORE"{ 
    close(fn) 
    fn="split-" ++n 
    printf "%s%s", hdr, $0 RS > fn 
} 
$1=="SD"{ 
    print > fn 
} 
END{close(fn)}' file 

In einer Zeile:

awk -F '|' 'NR<=3{hdr = hdr $0 RS} $1=="FORE"{close(fn); fn="split-" ++n; printf "%s%s", hdr, $0 RS > fn} $1=="SD"{print > fn} END{close(fn)}' file 
+0

Sie machen eine Datei pro FORE? Ich habe die Frage gelesen, als wolle ich eine Datei pro 20 MB, die auf FORE-Grenzen aufgeteilt ist. (Beachten Sie "Split auf Dateigröße" in der Spezifikation und den 20-MB-Wert in Pseudocode angegeben). –

+0

Ich könnte falsch liegen, da es sehr spät hier ist :) – anubhava

+1

(awk * ist * ein gutes Werkzeug für den Job; Ich bin fast versucht, meine Antwort für ksh93 Kompatibilität zu aktualisieren und zu testen, nur um eine Hoffnung zu haben mäßig wettbewerbsfähig auf Performance). –

0

Die Frage wäre mit Linien leichter sein, wie

FORE|20140508|20140214|\rSD|0|0039 - data|data|data|data|\rSD|0|0211 - data|data|data|data|\rSD|0|0039 - data|data|data|data|\rSD|0|0211 - data|data|data|data| 
FORE|20140508|20140214|\rSD|0|0039 - data|data|data|data|\rSD|0|0039 - data|data|data|data|\rSD|0|0211 - data|data|data|data| 

Erste Vorprozess die Datei mit awk Speichern der Header in einer temporären Datei und Verbinden von Zeilen mit diesen Beginnen Sie mit SD. Rufen Sie jetzt split -C 20m filename mit zusätzlichen Parametern, die Sie mögen. Weiter tr "\r" "\n" in verschiedene Zeilen und fügen Sie die Header in allen Dateien hinzu.

EDIT: Preprocessing für beigetreten Linien können mit

awk 'NR<=3 { print >> "filename.head" } 
    /^FORE/ { printf("%s%s",skipFirstNewline, $0); skipFirstNewline="\n" } 
    /^SD/ { printf("\r%s",$0) } 
    END{printf "\n" }' filename 

getan werden, wenn Sie die Ergebnisse überprüft, werden Sie durch den Wagen verwirren lassen kehrt \r. Ersetzen Sie daher \r temporär durch rr, wenn Sie die Ausgabe überprüfen möchten.

+0

Sehr gute Idee. Aber nur darum kämpfen, wie man den Vorverarbeitungsteil des Verbindens der Linien mit awk implementiert. Immer noch herausfinden – user3055262

Verwandte Themen