one-liner for separating iCal events by category

Attempting to move a friend from Outlook to Google Calendar, we exported the calendar to an iCal file.

I wanted to create separate Calendars for each event category.

I searched cpan for modules to read the file and found iCal::Parser, Text::vFile, and Text::vFile::asData.

I really didn't need to parse the data, because then I would have to figure out how to unparse it, and i didn't want the data to get messed up in the process.

I was definitely justified: simply doing iCal::Parser->new->parse($file) put my CPU at 3.5 load for 2+ minutes, whereas this one-liner takes a fraction of a second:

perl  -MIO::File -nE 'if(/BEGIN:VEVENT/){ warn("event not ended: $e") if defined $e; $e = $_ } elsif(/END:VEVENT/){ $e .= $_; $e =~ /^CATEGORIES:\s*(.+)\s*$/m; ($n=$1||"")=~s/\W+/_/g; ($fh{$n} ||= IO::File->new("cal-$n.txt", "w"))->print($e); $e = undef; } else { (defined($e) ? $e : $v) .= $_ } END { print $v }' outlook.ics > leftover.txt

Then a bit of verification:

$ expr `wc -l < outlook.ics` - `wc -l < leftover.txt`
16604

$ cat cal* | wc -l
16604

$ diff <(grep -h CATEGORIES cal* | sort | uniq -c) <(grep -h CATEGORIES outlook.ics | sort | uniq -c)

Then I split leftover.txt manually because I don't know that much about the iCal format and don't know what other possibilities there might be, but the file I had was pretty straightforward... there was a 20 line header, and 1 line tail ("END:VCALENDAR"), and everything in between was events.

$ for i in cal*; { cat head.txt $i tail.txt > icals/${i%.txt}.ics; }

Now I have separate calendar files for each category. Now, if only Google Calendar would let me upload them all at once and name them like I name my files... oh well. I've gained enough already, I can do this part manually.

Leave a comment

About Randy Stauner

user-pic perl -C -E 'say"i \x{2764} ",($^X =~ m#([^/]+)$#)'