У меня есть один файл с -|
разделителем после каждого раздела ... нужно создавать отдельные файлы для каждого раздела, используя unix.
пример входного файла
wertretr
ewretrtret
1212132323
000232
-|
ereteertetet
232434234
erewesdfsfsfs
0234342343
-|
jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|
Ожидаемый результат в файле 1
wertretr
ewretrtret
1212132323
000232
-|
Ожидаемый результат в файле 2
ereteertetet
232434234
erewesdfsfsfs
0234342343
-|
Ожидаемый результат в файле 3
jdhg3875jdfsgfd
sjdhfdbfjds
347674657435
-|
Ответы:
Один лайнер, без программирования. (кроме регулярного выражения и т. д.)
csplit --digits=2 --quiet --prefix=outfile infile "/-|/+1" "{*}"
протестировано на:
csplit (GNU coreutils) 8.30
Примечания по использованию на Apple Mac
«Для пользователей OS X обратите внимание, что версия
csplit
, поставляемая с ОС, не работает. Вам понадобится версия в coreutils (устанавливается через Homebrew), которая называетсяgcsplit
». - @Danial«Чтобы добавить, вы можете получить версию для OS X для работы (по крайней мере, с High Sierra). Вам просто нужно немного настроить аргументы
csplit -k -f=outfile infile "/-\|/+1" "{3}"
. Функции, которые, похоже, не работают, - это то"{*}"
, о чем я должен был указать количество разделителей, и необходимо добавить,-k
чтобы избежать удаления всех исходящих файлов, если он не может найти последний разделитель. Также, если вы хотите--digits
, вам нужно использовать-n
вместо этого. " - @Pebblисточник
--elide-empty-files
, иначе в конце будет пустой файл.--digits=2
контролирует количество цифр, используемых для нумерации выходных файлов (2 по умолчанию для меня, поэтому не обязательно).--quiet
подавляет вывод (тоже не обязательно или запрашивается здесь).--prefix
указывает префикс выходных файлов (по умолчанию xx). Таким образом, вы можете пропустить все параметры и получить выходные файлы вродеxx12
.csplit -k -f=outfile infile "/-\|/+1" "{3}"
. Функции, которые, похоже, не работают, - это то"{*}"
, что я должен был-k
указать количество разделителей и добавить, чтобы избежать удаления всех исходящих файлов, если он не может найти последний разделитель. Также, если вы хотите--digits
, вам нужно использовать-n
вместо этого.awk '{f="file" NR; print $0 " -|"> f}' RS='-\\|' input-file
Пояснение (отредактировано):
RS
является разделителем записей, и в этом решении используется расширение gnu awk, которое позволяет использовать более одного символа.NR
это номер записи.Оператор печати печатает запись, за которой следует,
" -|"
в файл, который содержит номер записи в своем имени.источник
RS
является разделителем записей, и в этом решении используется расширение gnu awk, которое позволяет использовать более одного символа. NR - номер рекорда. Оператор печати печатает запись, за которой следует "- |" в файл, в имени которого содержится номер записи.>
, так что вы можете построить ее как хотите. например,print $0 "-|" > "file" NR ".txt"
awk '{f="file" NR; print $0 " -|" > f}'
У Debian есть
csplit
, но я не знаю, является ли это общим для всех / большинства / других дистрибутивов. Если нет, то не должно быть слишком сложно отследить источник и скомпилировать его ...источник
csplit
находится в POSIX, я ожидаю, что он будет доступен практически во всех Unix-подобных системах.csplit --prefix gold-data - "/^==*$/
противcsplit --prefix gold-data - "/^=+$/
. По крайней мере, у GNU grep есть-e
.Я решил немного другую проблему, когда файл содержит строку с именем, в которую должен идти следующий текст. Этот код на Perl помогает мне:
#!/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 "$_"}; }
источник
while
петле. Если он находитmff
регулярное выражение в начале строки, он использует оставшуюся часть строки как имя файла, чтобы открыть и начать запись. Он никогда ничего не закрывает, поэтому дескрипторы файлов закончатся через несколько десятков.while
циклом и переключившись наwhile (<>)
У меня работает следующая команда. Надеюсь, это поможет.
awk 'BEGIN{file = 0; filename = "output_" file ".txt"} /-|/ {getline; file ++; filename = "output_" file ".txt"} {print $0 > filename}' input
источник
close
старый файл при запуске нового.if (file) close(filename);
перед присвоением новогоfilename
значения.; close(filename)
. Действительно просто, но это действительно исправляет приведенный выше примерВы также можете использовать awk. Я не очень знаком с awk, но мне показалось, что следующее работает. Он создал part1.txt, part2.txt, part3.txt и part4.txt. Обратите внимание, что последний созданный файл partn.txt пуст. Я не уверен, как это исправить, но уверен, что это можно сделать с помощью небольшой настройки. Есть какие-нибудь предложения?
awk_pattern файл:
BEGIN{ fn = "part1.txt"; n = 1 } { print > fn if (substr($0,1,2) == "-|") { close (fn) n++ fn = "part" n ".txt" } }
команда bash:
awk -f awk_pattern input.file
источник
Вот сценарий Python 3, который разбивает файл на несколько файлов на основе имени файла, указанного в разделителях. Пример входного файла:
# Ignored ######## FILTER BEGIN foo.conf This goes in foo.conf. ######## FILTER END # Ignored ######## FILTER BEGIN bar.conf This goes in bar.conf. ######## FILTER END
Вот сценарий:
#!/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)
Наконец, вот как вы его запускаете:
$ python3 script.py -i input-file.txt -o ./output-folder/
источник
Используйте,
csplit
если он у вас есть.Если нет, но у вас есть Python ... не используйте Perl.
Ленивое чтение файла
Ваш файл может быть слишком большим для одновременного хранения в памяти - предпочтительнее построчное чтение. Предположим, что входной файл назван «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"
источник
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 )
и отформатированная версия:
#!/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; )
источник
cat
cat
использования одного файла в любой ситуации. Существует вопрос о переполнении стека с дополнительным обсуждением (хотя принятый ответ - IMHO off); stackoverflow.com/questions/11710552/useless-use-of-catcsplit
, решение Awk, вероятно, будет намного предпочтительнее этого решения (даже если вы должны были исправить проблемы, о которых сообщает shellcheck.net и т. д.; обратите внимание, что в настоящее время оно не находит в нем всех ошибок).cat
прежнему бесполезен, а остальную часть скрипта можно значительно упростить и исправить; но все равно будет медленно. См., Например, stackoverflow.com/questions/13762625/…Это проблема, для которой я написал разделение контекста: 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
источник
csplit
утилиты. См . Ответ @ richard .Here is a perl code that will do the thing
#!/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);
источник