Jak podzielić duży plik tekstowy na mniejsze pliki o jednakowej liczbie wierszy?

514

Mam duży (według liczby wierszy) zwykły plik tekstowy, który chciałbym podzielić na mniejsze pliki, również według liczby wierszy. Więc jeśli mój plik ma około 2 mln linii, chciałbym podzielić go na 10 plików zawierających 200 000 linii lub 100 plików zawierających 20 000 linii (plus jeden plik z resztą; bycie równo podzielne nie ma znaczenia).

Mogę to zrobić dość łatwo w Pythonie, ale zastanawiam się, czy istnieje jakiś sposób ninja, aby to zrobić za pomocą narzędzi bash i unix (w przeciwieństwie do ręcznego zapętlania i liczenia / dzielenia linii).

Danben
źródło
2
Z ciekawości, po „rozdzieleniu”, jak je „połączyć”? Coś w stylu „cat part2 >> part1”? Czy jest jeszcze jakieś narzędzie ninja? masz ochotę zaktualizować swoje pytanie?
dlamotte
7
Podsumowując,cat part* > original
Mark Byers
9
tak kot jest skrótem do konkatenacji. Ogólnie apropos przydaje się do znajdowania odpowiednich poleceń. IE patrz wyniki: apropos split
pixelbeat
@pixelbeat To jest całkiem fajne, dzięki
Danben
3
Nawiasem mówiąc, użytkownicy OS X powinni upewnić się, że ich plik zawiera podział linii / wskaźniki końca linii (LF) w stylu LINUX lub UNIX zamiast MAC OS X - wskaźniki końca linii (CR) w stylu - podział i Polecenia csplit nie będą działać, jeśli podobnymi przerwami są Zwroty karetki zamiast LineFeeds. TextWrangler z oprogramowania BareBones może ci w tym pomóc, jeśli korzystasz z systemu Mac OS. Możesz wybrać, jak mają wyglądać znaki podziału linii. podczas zapisywania (lub Zapisz jako ...) plików tekstowych.

Odpowiedzi:

855

Czy spojrzałeś na polecenie podziału?

$ split --help
Usage: split [OPTION] [INPUT [PREFIX]]
Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default
size is 1000 lines, and default PREFIX is `x'.  With no INPUT, or when INPUT
is -, read standard input.

Mandatory arguments to long options are mandatory for short options too.
  -a, --suffix-length=N   use suffixes of length N (default 2)
  -b, --bytes=SIZE        put SIZE bytes per output file
  -C, --line-bytes=SIZE   put at most SIZE bytes of lines per output file
  -d, --numeric-suffixes  use numeric suffixes instead of alphabetic
  -l, --lines=NUMBER      put NUMBER lines per output file
      --verbose           print a diagnostic to standard error just
                            before each output file is opened
      --help     display this help and exit
      --version  output version information and exit

Możesz zrobić coś takiego:

split -l 200000 filename

który utworzy pliki zawierające 200 000 linii o nazwie xaa xab xac...

Inna opcja, podzielona według rozmiaru pliku wyjściowego (nadal dzieli się przy podziale wierszy):

 split -C 20m --numeric-suffixes input_filename output_prefix

tworzy pliki takie jak output_prefix01 output_prefix02 output_prefix03 ...każdy o maksymalnym rozmiarze 20 megabajtów.

Mark Byers
źródło
16
możesz także podzielić plik według rozmiaru: split -b 200m filename(m dla megabajtów, k dla kilobajtów lub brak sufiksu dla bajtów)
Abhi Beckert
136
Podział ze względu na rozmiar i zapewniają pliki są podzielone na podziały wiersza: split-C 200 nazwa_pliku
Clayton Stanley
2
split tworzy zniekształcone wyjście z wejściem Unicode (UTF-16). Przynajmniej w systemie Windows z wersją, którą mam.
Zawroty głowy
4
@geotheory, pamiętaj, aby postępować zgodnie z radą LeberMaca wcześniej w wątku na temat pierwszej konwersji zakończeń linii CR (Mac) na zakończenia linii LR (Linux) za pomocą TextWrangler lub BBEdit. Miałem dokładnie taki sam problem jak ty, dopóki nie znalazłem tej rady.
sstringer
6
-dopcja nie jest dostępna w OSX, użyj gsplitzamiast tego. Mam nadzieję, że to przydatne dla użytkowników komputerów Mac.
user5698801
80

Co powiesz na polecenie split ?

split -l 200000 mybigfile.txt
Robert Christie
źródło
39

Tak, jest splitpolecenie. Dzieli plik według linii lub bajtów.

$ split --help
Usage: split [OPTION]... [INPUT [PREFIX]]
Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default
size is 1000 lines, and default PREFIX is `x'.  With no INPUT, or when INPUT
is -, read standard input.

Mandatory arguments to long options are mandatory for short options too.
  -a, --suffix-length=N   use suffixes of length N (default 2)
  -b, --bytes=SIZE        put SIZE bytes per output file
  -C, --line-bytes=SIZE   put at most SIZE bytes of lines per output file
  -d, --numeric-suffixes  use numeric suffixes instead of alphabetic
  -l, --lines=NUMBER      put NUMBER lines per output file
      --verbose           print a diagnostic just before each
                            output file is opened
      --help     display this help and exit
      --version  output version information and exit

SIZE may have a multiplier suffix:
b 512, kB 1000, K 1024, MB 1000*1000, M 1024*1024,
GB 1000*1000*1000, G 1024*1024*1024, and so on for T, P, E, Z, Y.
Dave Kirby
źródło
Próbowałem georgec @ ATGIS25 ~ $ split -l 100000 /cygdrive/P/2012/Job_044_DM_Radio_Propogation/Working/FinalPropogation/TRC_Longlands/trc_longlands.txt ale nie ma plików podzielonych w katalogu - gdzie jest wyjście?
GeorgeC
1
Powinien być w tym samym katalogu. Na przykład, jeśli chcę podzielić przez 1 000 000 wierszy na plik, wykonaj następujące czynności: split -l 1000000 train_file train_file.w tym samym katalogu otrzymam train_file.aapierwszy milion, a następnie trail_file.abnastępny milion itd.
Będzie
1
@GeorgeC i można dostać katalogi zwyczaj wyjściowe z prefiksem: split input my/dir/.
Ciro Santilli 24 冠状 病 六四 事件 法轮功 24.04.16
15

posługiwać się split

Podziel plik na części o stałym rozmiarze, tworzy pliki wyjściowe zawierające kolejne sekcje INPUT (standardowe wejście, jeśli nie podano żadnego lub INPUT to `- ')

Syntax split [options] [INPUT [PREFIX]]

http://ss64.com/bash/split.html

zmbush
źródło
13

Posługiwać się:

sed -n '1,100p' filename > output.txt

Tutaj 1 i 100 to numery linii, w których będziesz przechwytywać output.txt.

Harshwardhan
źródło
Uzyskuje to tylko pierwsze 100 wierszy, musisz zapętlić go, aby sukcesywnie podzielić plik na następne 101..200 itd. Lub po prostu użyj, splittak jak mówią wszystkie najlepsze odpowiedzi tutaj.
tripleee
10

podziel plik „file.txt” na pliki 10000 linii:

split -l 10000 file.txt
ialqwaiz
źródło
9

split(z GNU coreutils, od wersji 8.8 z 2010-12-22 ) zawiera następujący parametr:

-n, --number=CHUNKS     generate CHUNKS output files; see explanation below

CHUNKS may be:
  N       split into N files based on size of input
  K/N     output Kth of N to stdout
  l/N     split into N files without splitting lines/records
  l/K/N   output Kth of N to stdout without splitting lines/records
  r/N     like 'l' but use round robin distribution
  r/K/N   likewise but only output Kth of N to stdout

W ten sposób split -n 4 input output.wygeneruje cztery pliki ( output.a{a,b,c,d}) z taką samą ilością bajtów, ale linie mogą być przerwane w środku.

Jeśli chcemy zachować pełne linie (tj. Dzielić na linie), to powinno działać:

split -n l/4 input output.

Powiązana odpowiedź: https://stackoverflow.com/a/19031247

Denilson Sá Maia
źródło
9

Jeśli chcesz po prostu podzielić x liczbę wierszy każdego pliku, podane odpowiedzi splitsą w porządku. Ale jestem ciekawy, czy nikt nie zwrócił uwagi na wymagania:

  • „bez konieczności ich liczenia” -> za pomocą wc + cut
  • „pozostała część w dodatkowym pliku” -> podział domyślnie

Nie mogę tego zrobić bez „wc + cut”, ale używam tego:

split -l  $(expr `wc $filename | cut -d ' ' -f3` / $chunks) $filename

Można to łatwo dodać do funkcji bashrc, aby można było po prostu wywołać przekazanie nazwy pliku i fragmentów:

 split -l  $(expr `wc $1 | cut -d ' ' -f3` / $2) $1

Jeśli chcesz tylko x fragmentów bez pozostałej części dodatkowego pliku, po prostu dostosuj formułę do sumowania (fragmenty - 1) na każdym pliku. Stosuję to podejście, ponieważ zwykle chcę po prostu x liczby plików zamiast x linii na plik:

split -l  $(expr `wc $1 | cut -d ' ' -f3` / $2 + `expr $2 - 1`) $1

Możesz dodać to do skryptu i nazwać to „ninja”, ponieważ jeśli nic nie odpowiada twoim potrzebom, możesz go zbudować :-)

m3nda
źródło
Lub po prostu użyj -nopcji split.
Amit Naidu
8

możesz także użyć awk

awk -vc=1 'NR%200000==0{++c}{print $0 > c".txt"}' largefile
ghostdog74
źródło
3
awk -v lines=200000 -v fmt="%d.txt" '{print>sprintf(fmt,1+int((NR-1)/lines))}'
Mark Edgar
0

HDFS wkleja mały plik i rozlewa do rozmiaru nieruchomości.

Ta metoda spowoduje przerwanie linii

split -b 125m compact.file -d -a 3 compact_prefix

Próbuję się zanurzyć i podzielić na około 128 MB na każdy plik.

# split into 128m ,judge sizeunit is M or G ,please test before use.

begainsize=`hdfs dfs -du -s -h /externaldata/$table_name/$date/ | awk '{ print $1}' `
sizeunit=`hdfs dfs -du -s -h /externaldata/$table_name/$date/ | awk '{ print $2}' `
if [ $sizeunit = "G" ];then
    res=$(printf "%.f" `echo "scale=5;$begainsize*8 "|bc`)
else
    res=$(printf "%.f" `echo "scale=5;$begainsize/128 "|bc`)  # celling ref http://blog.csdn.net/naiveloafer/article/details/8783518
fi
echo $res
# split into $res files with number suffix.  ref  http://blog.csdn.net/microzone/article/details/52839598
compact_file_name=$compact_file"_"
echo "compact_file_name :"$compact_file_name
split -n l/$res $basedir/$compact_file -d -a 3 $basedir/${compact_file_name}
Matiji66
źródło