Как разделить файл, используя границы ключевых слов

15

У меня есть файл VCF, который содержит множество VCARD.

При импорте vcf-файла в outlook кажется, что он импортирует только первую vcard.

Поэтому я хочу разделить их.

Учитывая, что vcard начинается с

BEGIN:VCARD

и заканчивается

END:VCARD

Каков наилучший способ разбить каждую vcard на отдельный файл?

Благодарность

ОБНОВИТЬ

Спасибо за все ответы. Как и в случае с вопросами такого рода, существуют различные способы снятия кожи с кошки. Вот причина, почему я выбрал тот, который я сделал.

ОКРУГЛЯТЬ

Вот обзор того, что мне понравилось в каждом ответе и что заставило меня выбрать один из них.

  • csplitМне очень понравилась лаконичность этого метода. Я просто хотел, чтобы он мог также установить расширение файла.
  • gawk: Он сделал все, что я просил об этом.
  • paralell: Работал. Но я должен был установить новые вещи. (также решил сделать новый / bin dir в моем домашнем каталоге)
  • perlМне понравилось, что он создал VCF на основе имени контакта. Но опция -o не сработала

Вывод

  • Так что первым делом было, perlпотому что он был немного сломан
  • Дальше было paralellпотому что пришлось устанавливать новые вещи
  • Далее было csplit, потому что, насколько я вижу, он не может создавать расширения для выходных файлов
  • Таким образом, награда вручается gawk за то, что она легкодоступна и достаточно универсальна, так что я могу немного порезать и изменить имя файла. Бонусные баллы cmpтоже :)
denormalizer
источник
Вы пытались использовать -b?
Игнасио Васкес-Абрамс

Ответы:

11

Вы можете использовать awk для работы:

$ curl -O https://raw.githubusercontent.com/qtproject/qt-mobility\
/d7f10927176b8c3603efaaceb721b00af5e8605b/demos/qmlcontacts/contents/\
example.vcf

$ gawk ' /BEGIN:VCARD/ { close(fn); ++a; fn=sprintf("card_%02d.vcf", a); 
        print "Writing: ", fn } { print $0 > fn; } ' example.vcf
Writing:  card_01.vcf
Writing:  card_02.vcf
Writing:  card_03.vcf
Writing:  card_04.vcf
Writing:  card_05.vcf
Writing:  card_06.vcf
Writing:  card_07.vcf
Writing:  card_08.vcf
Writing:  card_09.vcf

$ cat card_0* > all.vcf
$ cmp example.vcf all.vcf
$ echo $?
0

Детали

Строка awk работает следующим образом: aэто счетчик, который увеличивается в каждой BEGIN:VCARDстроке, и в то же время выходное имя файла создается с использованием sprintf (хранится в fn). Для каждой строки текущая строка ( $0) добавляется к текущему файлу (именованному fn).

Последнее echo $?означает, что он cmpбыл успешным, то есть все отдельные конкатенированные файлы совпадают с исходным примером vcf.

Обратите внимание, что перенаправление вывода в awk работает иначе, чем в shell. Это означает, что с помощью > fnawk сначала проверяется, открыт ли файл. Если он уже открыт, то к нему добавляется awk . Если это не так, он открывает и усекает его.

Из-за этой логики перенаправления мы должны явно закрывать неявно открытые файлы, так как в противном случае вызов достигнет предела открытого файла в случаях, когда входной файл содержит много записей.

maxschlepzig
источник
Вам нужно будет закрыть файл, чтобы избежать ошибки слишком большого количества открытых файлов в awk. stackoverflow.com/questions/32878146/… Итак, команда становится: gawk '/ BEGIN: VCARD / {close (fn); ++ а; fn = sprintf ("карточка_% 02d.vcf", а); print "Writing:", fn} {print $ 0 >> fn; } 'example.vcf
Дэн Беннетт
@DanBennett Большое спасибо за подсказку! Я обновил свой ответ, а также упростил примечания, связанные с логикой перенаправления / исправленным перенаправлением.
maxschlepzig
11
csplit -f vcard input.txt -z '/END:VCARD/+1' '{*}'
Игнасио Васкес-Абрамс
источник
5

Версия csplit для Gnu может установить расширение - ответ Игнасио, я думаю, является наиболее лаконичным, для получения расширения требуется лишь последняя настройка - используя формат «printf»:

csplit -f vcard -b %02d.vcard input.txt -z '/END:VCARD/+1' '{*}'

Вот соответствующий фрагмент со csplitстраницы руководства gnu :

   -b, --suffix-format=FORMAT
          use sprintf FORMAT instead of %02d
Keithel
источник
Я использовал Mac, и мне потребовалось некоторое время, чтобы понять, как использовать вместо него gcsplit, но как только я это сделал, мне помог этот ответ.
Люк Гедеон
4

Вы можете использовать этот скрипт, чтобы сделать работу. Это называется split-vcf-file .

Пример использования

$ split_vcf.pl 

Error! Input VCF filename missing,  -i

Usage: perl split_vcf.pl -i input_file -o output_dir [OPTION]

    -v,         Verbosity levels, 1-3

Чтобы запустить скрипт:

mkdir vcf_files
split_vcf.pl  -i current.vcf -o vcf_files
SLM
источник
split_vcf.pl - версия для Windows. для unix измените sub make_filename, который добавляет «\» в имена файлов.
J Дан
4

Используя GNU Parallel вы можете сделать:

cat foo.vcf | parallel --pipe -N1 --recstart BEGIN:VCARD 'cat >{#}'

Или, если вы можете опровергнуть http://oletange.blogspot.com/2013/10/useless-use-of-cat.html, вы можете использовать это вместо:

< foo.vcf parallel --pipe -N1 --recstart BEGIN:VCARD 'cat >{#}'

Смотрите больше примеров: http://www.gnu.org/software/parallel/man.html

Посмотрите вступительные видеоролики: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

10 секунд установки:

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 3374ec53bacb199b245af2dda86df6c9
12345678 3374ec53 bacb199b 245af2dd a86df6c9
$ md5sum install.sh | grep 029a9ac06e8b5bc6052eac57b2c3c9ca
029a9ac0 6e8b5bc6 052eac57 b2c3c9ca
$ sha512sum install.sh | grep f517006d9897747bed8a4694b1acba1b
40f53af6 9e20dae5 713ba06c f517006d 9897747b ed8a4694 b1acba1b 1464beb4
60055629 3f2356f3 3e9c4e3c 76e3f3af a9db4b32 bd33322b 975696fc e6b23cfb
$ bash install.sh
Оле Танге
источник