Mam jeden plik z -|
separatorem po każdej sekcji ... muszę utworzyć osobne pliki dla każdej sekcji używając unixa.
przykład pliku wejściowego
wertretr
ewretrtret
1212132323
000232
-|
ereteertetet
232434234
erewesdfsfsfs
0234342343
-|
jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|
Oczekiwany wynik w pliku 1
wertretr
ewretrtret
1212132323
000232
-|
Oczekiwany wynik w pliku 2
ereteertetet
232434234
erewesdfsfsfs
0234342343
-|
Oczekiwany wynik w pliku 3
jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|
Odpowiedzi:
Jedna linijka, bez programowania. (oprócz wyrażenia regularnego itp.)
csplit --digits=2 --quiet --prefix=outfile infile "/-|/+1" "{*}"
testowano na:
csplit (GNU coreutils) 8.30
Uwagi dotyczące używania na Apple Mac
„W przypadku użytkowników OS X, pamiętaj, że wersja
csplit
tego systemu operacyjnego nie działa. Będziesz potrzebować wersji w coreutils (do zainstalowania przez Homebrew), która nazywa sięgcsplit
”. - @Danial„Wystarczy dodać, że możesz sprawić, aby wersja dla OS X działała (przynajmniej z High Sierra). Wystarczy trochę poprawić argumenty
csplit -k -f=outfile infile "/-\|/+1" "{3}"
. Funkcje, które wydają się nie działać, to takie"{*}"
, musiałem być konkretny liczbę separatorów, które trzeba dodać,-k
aby uniknąć usunięcia wszystkich plików wyjściowych, jeśli nie może znaleźć końcowego separatora. Jeśli chcesz--digits
, musisz użyć-n
zamiast tego. " - @Pebblźródło
--elide-empty-files
, w przeciwnym razie na końcu będzie pusty plik.--digits=2
kontroluje liczbę cyfr użytych do numerowania plików wyjściowych (dla mnie 2 jest domyślna, więc nie jest to konieczne).--quiet
blokuje wyjście (również nie jest naprawdę konieczne ani nie jest tutaj wymagane).--prefix
określa przedrostek plików wyjściowych (domyślnie xx). Możesz więc pominąć wszystkie parametry i otrzymać pliki wyjściowe, takie jakxx12
.csplit -k -f=outfile infile "/-\|/+1" "{3}"
. Funkcje, które wydają się nie działać"{*}"
, to: musiałem określić liczbę separatorów i musiałem je dodać,-k
aby uniknąć usunięcia wszystkich plików wyjściowych, jeśli nie może znaleźć końcowego separatora. Jeśli chcesz--digits
, musisz-n
zamiast tego użyć .awk '{f="file" NR; print $0 " -|"> f}' RS='-\\|' input-file
Wyjaśnienie (zredagowano):
RS
jest separatorem rekordów, a to rozwiązanie wykorzystuje rozszerzenie gnu awk, które pozwala na użycie więcej niż jednego znaku.NR
to numer rekordu.Instrukcja print drukuje rekord, po którym następuje,
" -|"
do pliku, który zawiera numer rekordu w swojej nazwie.źródło
RS
jest separatorem rekordów, a to rozwiązanie wykorzystuje rozszerzenie gnu awk, które pozwala na użycie więcej niż jednego znaku. NR to numer rekordu. Instrukcja print drukuje rekord, po którym następuje „- |” do pliku, który zawiera numer rekordu w swojej nazwie.>
, więc możesz go skonstruować w dowolny sposób . np.print $0 "-|" > "file" NR ".txt"
awk '{f="file" NR; print $0 " -|" > f}'
Debian tak
csplit
, ale nie wiem, czy jest to wspólne dla wszystkich / większości / innych dystrybucji. Jeśli nie, to nie powinno być zbyt trudno wyśledzić źródło i skompilować je ...źródło
csplit
jest w POSIX, spodziewałbym się, że będzie dostępny w zasadzie we wszystkich systemach uniksopodobnych.csplit --prefix gold-data - "/^==*$/
vscsplit --prefix gold-data - "/^=+$/
. Przynajmniej GNU grep ma-e
.Rozwiązałem nieco inny problem, w którym plik zawiera wiersz z nazwą miejsca, w którym powinien znajdować się następujący tekst. Ten kod w Perlu załatwia sprawę:
#!/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 "$_"}; }
źródło
while
pętli. Jeśli znajdziemff
wyrażenie regularne na początku wiersza, użyje reszty wiersza jako nazwy pliku do otwarcia i rozpoczęcia zapisu. Nigdy niczego nie zamyka, więc po kilkudziesięciu zabraknie uchwytów plików.while
pętlą i przejście dowhile (<>)
Następujące polecenie działa dla mnie. Mam nadzieję, że to pomoże.
awk 'BEGIN{file = 0; filename = "output_" file ".txt"} /-|/ {getline; file ++; filename = "output_" file ".txt"} {print $0 > filename}' input
źródło
close
starego pliku podczas uruchamiania nowego.if (file) close(filename);
przed przypisaniem nowejfilename
wartości.; close(filename)
. Naprawdę proste, ale naprawdę rozwiązuje powyższy przykładMożesz także użyć awk. Nie jestem zaznajomiony z awk, ale wydaje mi się, że działają w moim przypadku poniższe. Wygenerował part1.txt, part2.txt, part3.txt i part4.txt. Zwróć uwagę, że ostatni generowany przez to plik partn.txt jest pusty. Nie jestem pewien, jak to naprawić, ale jestem pewien, że można to zrobić przy niewielkich poprawkach. Jakieś sugestie ktoś?
plik awk_pattern:
BEGIN{ fn = "part1.txt"; n = 1 } { print > fn if (substr($0,1,2) == "-|") { close (fn) n++ fn = "part" n ".txt" } }
bash polecenie:
awk -f awk_pattern input.file
źródło
Oto skrypt Python 3, który dzieli plik na wiele plików na podstawie nazwy pliku podanej przez ograniczniki. Przykładowy plik wejściowy:
# Ignored ######## FILTER BEGIN foo.conf This goes in foo.conf. ######## FILTER END # Ignored ######## FILTER BEGIN bar.conf This goes in bar.conf. ######## FILTER END
Oto skrypt:
#!/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)
Wreszcie, jak to uruchomić:
$ python3 script.py -i input-file.txt -o ./output-folder/
źródło
Użyj,
csplit
jeśli go masz.Jeśli nie, ale masz Pythona ... nie używaj Perla.
Leniwe czytanie pliku
Twój plik może być zbyt duży, aby przechowywać go w pamięci od razu - preferowane może być czytanie wiersz po wierszu. Załóżmy, że plik wejściowy nosi nazwę „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"
źródło
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 )
i wersja sformatowana:
#!/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; )
źródło
cat
cat
pojedynczego pliku w każdej sytuacji. Jest pytanie przepełnienia stosu z większą dyskusją (chociaż zaakceptowana odpowiedź to IMHO wyłączone); stackoverflow.com/questions/11710552/useless-use-of-catcsplit
, rozwiązanie Awk jest prawdopodobnie o wiele lepsze niż to rozwiązanie (nawet jeśli miałbyś naprawić problemy zgłoszone przez shellcheck.net itp; zauważ, że obecnie nie znajduje ono wszystkich błędów).cat
jest on nadal bezużyteczny, a resztę skryptu można by znacznie uprościć i poprawić; ale nadal będzie powolny. Patrz np stackoverflow.com/questions/13762625/...To jest rodzaj problemu, dla którego napisałem podział kontekstu: 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
źródło
csplit
narzędzia. Zobacz odpowiedź @ richard .Oto kod Perla, który zrobi to
#!/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);
źródło