Gegeben: Eine große Textdatendatei (z. B. CSV-Format) mit einer speziellen ersten Zeile (z. B. Feldnamen).
Gesucht: Entspricht dem split -l
Befehl coreutils , jedoch mit der zusätzlichen Anforderung, dass die Kopfzeile aus der Originaldatei am Anfang jedes der resultierenden Teile angezeigt wird.
Ich vermute eine Erfindung split
und head
werde den Trick machen?
split
nicht wahr?cat a b c > reconstructed
. Überflüssige Zeilen in der Datei bedeuten, dass der normale Rekonstruktionsansatz die Originaldatei nicht reproduziert.unsplit --remove-header
" Dienstprogramm gedacht! Aber im Ernst,split
wenn es eine "Repeat-Header" -Option geben sollte, sollte es immer noch standardmäßig sein aktuelles Verhalten verwenden. Sie würden nur Header-Inhalte verwenden, wenn Sie es wirklich wollten.--keep-first N
wäre eine nette Option,split
die sowohl im Zeilen- als auch im Byte-Modus nützlich wäreAntworten:
Dies ist Robhruskas Drehbuch, das ein bisschen aufgeräumt wurde:
tail -n +2 file.txt | split -l 4 - split_ for file in split_* do head -n 1 file.txt > tmp_file cat "$file" >> tmp_file mv -f tmp_file "$file" done
Ich entfernte
wc
,cut
,ls
undecho
an den Orten , wo sie nicht notwendig sind. Ich habe einige der Dateinamen geändert, um sie ein wenig aussagekräftiger zu machen. Ich habe es in mehrere Zeilen aufgeteilt, um das Lesen zu erleichtern.Wenn Sie Lust haben, können Sie einen temporären Dateinamen verwenden
mktemp
odertempfile
erstellen, anstatt einen fest codierten zu verwenden.Bearbeiten
Mit GNU ist
split
dies möglich:split_filter () { { head -n 1 file.txt; cat; } > "$FILE"; }; export -f split_filter; tail -n +2 file.txt | split --lines=4 --filter=split_filter - split_
Aus Gründen der Lesbarkeit aufgeschlüsselt:
split_filter () { { head -n 1 file.txt; cat; } > "$FILE"; } export -f split_filter tail -n +2 file.txt | split --lines=4 --filter=split_filter - split_
Wenn
--filter
angegeben, wirdsplit
der Befehl (in diesem Fall eine Funktion, die exportiert werden muss) für jede Ausgabedatei ausgeführt und die VariableFILE
in der Befehlsumgebung auf den Dateinamen gesetzt.Ein Filterskript oder eine Funktion kann jede gewünschte Manipulation des Ausgabeinhalts oder sogar des Dateinamens vornehmen. Ein Beispiel für Letzteres könnte die Ausgabe an einen festen Dateinamen in einem Variablenverzeichnis sein:
> "$FILE/data.dat"
zum Beispiel.quelle
for $part in (split -l 1000 myfile); cat <(head -n1 myfile) $part > myfile.$part; done
split
notwendigerweise nicht ausgegeben wirdstdout
.split
konnte geben die Namen der Dateien auf stdout, wenn auch (solange wir diskutieren , wassplit
sollte tun :-)Sie können die neue --filter-Funktionalität in GNU coreutils split> = 8.13 (2011) verwenden:
tail -n +2 FILE.in | split -l 50 - --filter='sh -c "{ head -n1 FILE.in; cat; } > $FILE"'
quelle
tail -n +2 FILE.in | split -d --lines 50 - --filter='bash -c "{ head -n1 ${FILE%.*}; cat; } > $FILE"' FILE.in.x
Sie können [mg] awk verwenden:
awk 'NR==1{ header=$0; count=1; print header > "x_" count; next } !( (NR-1) % 100){ count++; print header > "x_" count; } { print $0 > "x_" count }' file
100 ist die Anzahl der Zeilen jeder Scheibe. Es erfordert keine temporären Dateien und kann in eine einzelne Zeile gestellt werden.
quelle
Dieser Einzeiler teilt die große CSV in 999 Datensätze auf, wobei sich die Kopfzeile oben befindet (also 999 Datensätze + 1 Kopfzeile = 1000 Zeilen).
cat bigFile.csv | parallel --header : --pipe -N999 'cat >file_{#}.csv'
Basierend auf der Antwort von Ole Tange. (Zu Oles Antwort: Sie können die Zeilenanzahl nicht mit Pipepart verwenden.)
quelle
brew install parallel
auf macOS. Klappt wunderbar!Ich bin ein Neuling, wenn es um Bash-Fu geht, aber ich konnte diese Monstrosität mit zwei Befehlen erfinden. Ich bin sicher, es gibt elegantere Lösungen.
$> tail -n +2 file.txt | split -l 4 $> for file in `ls xa*`; do echo "`head -1 file.txt`" > tmp; cat $file >> tmp; mv -f tmp $file; done
Dies setzt voraus, dass Ihre Eingabedatei lautet
file.txt
, dass Sie dasprefix
Argument nicht verwendensplit
und in einem Verzeichnis arbeiten, in dem keine anderen Dateien vorhanden sind, die mitsplit
dem Standardausgabeformat beginnenxa*
. Ersetzen Sie außerdem die '4' durch die gewünschte Größe der Trennlinie.quelle
Dies ist eine robustere Version von Denis Williamsons Skript. Das Skript erstellt viele temporäre Dateien, und es wäre eine Schande, wenn sie herumliegen würden, wenn der Lauf unvollständig wäre. Fügen wir also die Signalüberwachung hinzu (siehe http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html und dann http://tldp.org/LDP/abs/html/debugging.html ) und Entfernen Sie unsere temporären Dateien. Dies ist sowieso eine bewährte Methode.
trap 'rm split_* tmp_file ; exit 13' SIGINT SIGTERM SIGQUIT tail -n +2 file.txt | split -l 4 - split_ for file in split_* do head -n 1 file.txt > tmp_file cat $file >> tmp_file mv -f tmp_file $file done
Ersetzen Sie '13' durch einen beliebigen Rückkehrcode. Oh, und Sie sollten wahrscheinlich sowieso mktemp verwenden (wie einige bereits vorgeschlagen haben), also entfernen Sie 'tmp_file "aus dem rm in der Trap-Zeile. Weitere Signale zum Abfangen finden Sie auf der Signal-Manpage.
quelle
Ich mochte die awk-Version von marco, die aus diesem vereinfachten Einzeiler übernommen wurde, bei dem Sie die geteilte Fraktion ganz einfach so detailliert angeben können, wie Sie möchten:
awk 'NR==1{print $0 > FILENAME ".split1"; print $0 > FILENAME ".split2";} NR>1{if (NR % 10 > 5) print $0 >> FILENAME ".split1"; else print $0 >> FILENAME ".split2"}' file
quelle
Ich mochte die Versionen von Rob und Dennis so sehr, dass ich sie verbessern wollte.
Hier ist meine Version:
in_file=$1 awk '{if (NR!=1) {print}}' $in_file | split -d -a 5 -l 100000 - $in_file"_" # Get all lines except the first, split into 100,000 line chunks for file in $in_file"_"* do tmp_file=$(mktemp $in_file.XXXXXX) # Create a safer temp file head -n 1 $in_file | cat - $file > $tmp_file # Get header from main file, cat that header with split file contents to temp file mv -f $tmp_file $file # Overwrite non-header containing file with header-containing file done
Unterschiede:
awk
statttail
wegen derawk
besseren Leistunghead | cat
Zeile anstelle von zwei Zeilenquelle
Verwenden Sie GNU Parallel:
parallel -a bigfile.csv --header : --pipepart 'cat > {#}'
Wenn Sie für jedes Teil einen Befehl ausführen müssen, kann GNU Parallel auch dabei helfen:
Wenn Sie in 2 Teile pro CPU-Kern aufteilen möchten (z. B. 24 Kerne = 48 gleich große Teile):
Wenn Sie in 10-MB-Blöcke aufteilen möchten:
quelle
Unten finden Sie einen 4-Liner, mit dem Sie eine bigfile.csv in mehrere kleinere Dateien aufteilen und den CSV-Header beibehalten können. Verwendet nur integrierte Bash-Befehle (head, split, find, grep, xargs und sed), die auf den meisten * nix-Systemen funktionieren sollten. Sollte auch unter Windows funktionieren, wenn Sie mingw-64 / git-bash installieren.
Zeile für Zeile Erklärung:
quelle
Inspiriert von @ Arkadys Kommentar zu einem Einzeiler.
split
Der Dateiname wird nicht angezeigt, aber mit dieser--additional-suffix
Option können wir einfach steuern, was zu erwarten istrm $part
(setzt keine Dateien mit demselben Suffix voraus)MYFILE=mycsv.csv && for part in $(split -n4 --additional-suffix=foo $MYFILE; ls *foo); do cat <(head -n1 $MYFILE) $part > $MYFILE.$part; rm $part; done
Beweis:
und natürlich wird
head -2 *foo
der Header hinzugefügt.quelle
Eine einfache, aber vielleicht nicht so elegante Methode: Schneiden Sie den Header vorher ab, teilen Sie die Datei und fügen Sie dann den Header jeder Datei mit cat oder einer beliebigen Datei, die sie einliest, wieder zusammen.
quelle