14

I have a vcf file that contains numerous vcards.

When importing the vcf file to outlook it seems to only import the first vcard.

Hence I want to split them up.

Given that a vcard starts with

BEGIN:VCARD

and ends with

END:VCARD

What is the best way to split each vcard into it's own file.

Thanks

UPDATE

Thanks for all the responses. As with questions of this nature there's various ways to skin a cat. Here's the reasoning why I chose the one I did.

ROUND-UP

Here's a roundup of what I liked from each answer and what drove me to select one of them.

  • csplit: I really really liked the conciseness of this method. I just wished it was able to also set the file extension.
  • gawk: It did everything i asked of it.
  • paralell: Worked. But I had to install new things. (it also decided to make a new /bin dir in my home dir)
  • perl: I liked that it created vcf based on contact's name. But the -o option didn't really work

Conclusion

  • So the first one to go was perl because it was a bit broken
  • Next was paralell because I had to install new things
  • Next was csplit, because as far as I can see it can't create extensions on the output files
  • So the award goes to gawk, for being a utility that's readily available, and versatile enough that I can chop and change the file name a bit. Bonus marks for cmp too :)
11

You can use awk for the job:

$ 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

Details

The awk line works like this: a is counter that is incremented on each BEGIN:VCARD line and at the same time the output filename is constructed using sprintf (stored in fn). For each line the current line ($0) is appended to the current file (named fn).

The last echo $? means that the cmp was successful, i.e. all single files concatenated are equal to the original example vcf example.

Note that the output redirection in awk works differently than in shell. That means that with > fn awk first checks if the file is already open. If it is already open then awk appends to it. If it is not then it opens and truncates it.

Because of this redirection logic we have to explicitly close the implicitly opened files, since otherwise the call would hit the open file limit in cases where the input file contains many records.

  • You'll need to close the file to avoid a too many open files error in awk. stackoverflow.com/questions/32878146/… So the command becomes: gawk ' /BEGIN:VCARD/ { close(fn); ++a; fn=sprintf("card_%02d.vcf", a); print "Writing: ", fn } { print $0 >> fn; } ' example.vcf – Dan Bennett Oct 15 '19 at 13:10
  • @DanBennett Thank you very much for the hint! I've updated my answer and also simplified the redirection logic/fixed redirection related notes. – maxschlepzig Oct 26 '19 at 9:14
11
csplit -f vcard input.txt -z '/END:VCARD/+1' '{*}'
5

The Gnu version of csplit can set the extension - Ignacio's answer I think is the most concise, it just needs that last bit of tweaking to get the extension - using 'printf' format:

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

Here's the relevant snippet from the gnu csplit man page:

   -b, --suffix-format=FORMAT
          use sprintf FORMAT instead of %02d
  • I was using mac and it took me a while to figure out to use gcsplit instead, but once I did this answer helped me. – Luke Gedeon Feb 20 '19 at 1:18
4

You can use this script to do the job. It's called split-vcf-file.

Example usage

$ 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

To run the script:

mkdir vcf_files
split_vcf.pl  -i current.vcf -o vcf_files
  • split_vcf.pl is a windows version. for unix modify the sub make_filename which was adding a "\" in the filenames. – J Dan Nov 7 '16 at 22:39
4

Using GNU Parallel you can do:

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

Or if you can refute http://oletange.blogspot.com/2013/10/useless-use-of-cat.html you can use this instead:

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

See more examples: http://www.gnu.org/software/parallel/man.html

Watch the intro videos: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

10 seconds installation:

$ (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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.