Gegeven:één groot tekstbestand (bijv. CSV-indeling) met een ‘speciale’ eerste regel (bijv. veldnamen).
Gezocht:een equivalent van de opdracht coreutils split -l
, maar met de aanvullende vereiste dat de kopregel van het originele bestand aan het begin van elk van de resulterende stukken.
Ik vermoed dat een verzinsel van split
en head
het zal doen?
Antwoord 1, autoriteit 100%
Dit is het script van robhruskawat opgeschoond:
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
Ik heb wc
, cut
, ls
en echo
verwijderd op de plaatsen waar ze niet nodig zijn. Ik heb enkele bestandsnamen gewijzigd om ze een beetje betekenisvoller te maken. Ik heb het in meerdere regels verdeeld om het gemakkelijker leesbaar te maken.
Als je zin hebt, kun je mktemp
of tempfile
gebruiken om een tijdelijke bestandsnaam te maken in plaats van een hardgecodeerde naam te gebruiken.
Bewerken
Met GNU split
is het mogelijk om dit te doen:
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_
Uitgesplitst voor leesbaarheid:
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_
Als --filter
is opgegeven, voert split
de opdracht uit (in dit geval een functie die moet worden geëxporteerd) voor elk uitvoerbestand en stelt de variabele FILE
, in de opdrachtomgeving, naar de bestandsnaam.
Een filterscript of -functie kan elke gewenste manipulatie doen aan de uitvoerinhoud of zelfs de bestandsnaam. Een voorbeeld van het laatste zou kunnen zijn om uit te voeren naar een vaste bestandsnaam in een variabele directory: > "$FILE/data.dat"
bijvoorbeeld.
Antwoord 2, autoriteit 34%
Deze one-liner splitst de grote csv in stukjes van 999 records, waarbij de koprij bovenaan elke record behouden blijft (dus 999 records + 1 kop = 1000 rijen)
cat bigFile.csv | parallel --header : --pipe -N999 'cat >file_{#}.csv'
Gebaseerd op het antwoord van Ole Tange.
(over het antwoord van Ole: je kunt het aantal regels niet gebruiken met pipepart)
Bekijk de opmerkingen voor enkele tips voor het installeren van parallel
Antwoord 3, autoriteit 22%
Je zou de nieuwe –filter functionaliteit in GNU coreutils split >= 8.13 (2011) kunnen gebruiken:
tail -n +2 FILE.in | split -l 50 - --filter='sh -c "{ head -n1 FILE.in; cat; } > $FILE"'
Antwoord 4, autoriteit 21%
U kunt [mg]awk:
. gebruiken
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 is het aantal regels van elk segment.
Het vereist geen tijdelijke bestanden en kan op een enkele regel worden geplaatst.
Antwoord 5, autoriteit 12%
Ik ben een beginneling als het gaat om Bash-fu, maar ik heb dit twee-commando-monster kunnen verzinnen. Ik weet zeker dat er elegantere oplossingen zijn.
$> 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
Dit veronderstelt dat uw invoerbestand file.txt
is, dat u het argument prefix
niet gebruikt om split
te gebruiken, en dat u werken in een map die geen andere bestanden heeft die beginnen met split
‘s standaard xa*
uitvoerformaat. Vervang ook de ‘4’ door de gewenste grootte van de gesplitste lijn.
Antwoord 6, autoriteit 4%
Gebruik GNU Parallel:
parallel -a bigfile.csv --header : --pipepart 'cat > {#}'
Als je een commando moet uitvoeren op elk van de onderdelen, dan kan GNU Parallel je daarbij ook helpen:
parallel -a bigfile.csv --header : --pipepart my_program_reading_from_stdin
parallel -a bigfile.csv --header : --pipepart --fifo my_program_reading_from_fifo {}
parallel -a bigfile.csv --header : --pipepart --cat my_program_reading_from_a_file {}
Als u wilt splitsen in 2 delen per CPU-kern (bijvoorbeeld 24 kernen = 48 gelijke delen):
parallel --block -2 -a bigfile.csv --header : --pipepart my_program_reading_from_stdin
Als je wilt opsplitsen in blokken van 10 MB:
parallel --block 10M -a bigfile.csv --header : --pipepart my_program_reading_from_stdin
Antwoord 7, autoriteit 3%
Dit is een robuustere versie van het script van Denis Williamson. Het script maakt veel tijdelijke bestanden aan en het zou zonde zijn als ze zouden blijven rondslingeren als de run onvolledig was. Laten we dus signaaltrapping toevoegen (zie http://tldp.org/ LDP/Bash-Beginners-Guide/html/sect_12_02.htmlen dan http ://tldp.org/LDP/abs/html/debugging.html) en verwijder onze tijdelijke bestanden; dit is sowieso een best practice.
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
Vervang ’13’ door de gewenste retourcode. Oh, en je zou waarschijnlijk sowieso mktemp moeten gebruiken (zoals sommigen al hebben gesuggereerd), dus ga je gang en verwijder ‘tmp_file’ uit de rm in de trap-regel. Zie de signal man-pagina voor meer signalen om te vangen.
Antwoord 8, autoriteit 3%
Ik vond de awk-versie van marco leuk, hieruit overgenomen een vereenvoudigde one-liner waar je de gesplitste fractie gemakkelijk zo gedetailleerd kunt specificeren als je wilt:
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
Antwoord 9, autoriteit 3%
Ik vond de versies van Rob en Dennis zo goed dat ik ze wilde verbeteren.
Hier is mijn versie:
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
Verschillen:
- in_file is het bestandsargument dat u wilt splitsen met behoud van headers
- Gebruik
awk
in plaats vantail
omdatawk
betere prestaties levert - opgesplitst in 100.000 regelbestanden in plaats van 4
- Gesplitste bestandsnaam wordt ingevoerd als bestandsnaam, aangevuld met een onderstrepingsteken en cijfers (maximaal 99999 – van het “-d -a 5” split-argument)
- Gebruik MKTEMP om de tijdelijke bestanden veilig af te handelen
- Gebruik enkele
head | cat
Lijn in plaats van twee lijnen
Antwoord 10, Autoriteit 3%
Hieronder is een 4 voering die kan worden gebruikt om een BIGFILE.CSV in meerdere kleinere bestanden te splitsen en de CSV-header te behouden. Gebruikt alleen ingebouwde bash-opdrachten (hoofd, split, find, grep, xargs en sed) die op de meeste * nix-systemen zou moeten werken. Moet ook werken op Windows als u MINGW-64 / GIT-bash installeert.
CSVHADEER = `Head -1 bigfile.csv` Split -d -L10000 bigfile.csv smallfile_ Zoek. | grep smallfile_ | XARGS SED -I "1S / ^ / $ CSVHEADER \ N /" sed -i '1d' smallfile_00
Lijn op lijnuitleg:
- Leg de koptekst vast aan een variabele genaamd CSVHearer
- Split de bigfile.csv in een aantal kleinere bestanden met prefix smallfile _
- Zoek alle smallfiles en plaats de CSVHADEER in de eerste regel met xargs en sed -i . Merk op dat u SED binnen “Dubbele Quotes” moet gebruiken om variabelen te gebruiken.
- Het eerste bestand met de naam SmallFile_00 heeft nu overbodig headers op lijnen 1 en 2 (uit de originele gegevens en van de SED-header-inzetstuk in stap 3). We kunnen de overtollige koptekst verwijderen met SED -I ‘1D’ -opdracht.
Antwoord 11
Geïnspireerd door @ arkady’s opmerking op een one-liner.
- MyFile-variabele om simpelweg de boilerplaat te verminderen
split
toont geen bestandsnaam, maar de--additional-suffix
Optie stelt ons in staat om gemakkelijk te regelen wat u kunt verwachten- Verwijdering van tussenliggende bestanden via
rm $part
(veronderstelt geen bestanden met hetzelfde achtervoegsel)
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
Bewijs:
-rw-rw-r-- 1 ec2-user ec2-user 32040108 Jun 1 23:18 mycsv.csv.xaafoo
-rw-rw-r-- 1 ec2-user ec2-user 32040108 Jun 1 23:18 mycsv.csv.xabfoo
-rw-rw-r-- 1 ec2-user ec2-user 32040108 Jun 1 23:18 mycsv.csv.xacfoo
-rw-rw-r-- 1 ec2-user ec2-user 32040110 Jun 1 23:18 mycsv.csv.xadfoo
en natuurlijk head -2 *foo
om te zien dat de koptekst wordt toegevoegd.
Antwoord 12
Een eenvoudige maar misschien niet zo elegant: snijd de kop van tevoren af, split het bestand en ga vervolgens opnieuw in de kop op elk bestand met kat, of met welk bestand het ook is.
Dus zoiets als:
- head -n1 file.txt & gt; header.txt
- Split -l file.txt
- Cat header.txt f1.txt