Replacing end of line characters

Hello,

I'm looking for some help in renaming data-timestamps stored within different calendar directories/files.

The calendar directory has hundreds of ICS files:

~/Library/Calendars/F494C908.calendar/Events/92B65E439AAC.ics
~/Library/Calendars/F494C908.calendar/Events/DE7867E61969.ics
~/Library/Calendars/KB67E731.calendar/Events/40bb3f34b060.ics
~/Library/Calendars/KB67E731.calendar/Events/901DB10BBCD3.ics
~/Library/Calendars/86SC23B7.calendar/Events/c7d53a321400.ics
...

A single ICS file usually holds the following example data:

BEGIN:VCALENDAR
CALSCALE:GREGORIAN
BEGIN:VEVENT
TRANSP:OPAQUE
DTEND;TZID=Europe/London:20131017T154059
DTSTAMP:20140405T115011Z
LOCATION:London
DESCRIPTION:Test
SEQUENCE:10
SUMMARY:Test
LAST-MODIFIED:20131017T122550Z
DTSTART;TZID=Europe/London:20131017T151512
CREATED:20131017T122550Z
END:VEVENT
END:VCALENDAR

My goal is to rename any DTSTART/DTEND line in all directories and files that doesn't end with 00 to end (replace) with 00

DTSTART;TZID=Europe/London:20131017T151512
should become
DTSTART;TZID=Europe/London:20131017T151500

as well as
DTEND;TZID=Europe/London:20131017T154059
should become
DTEND;TZID=Europe/London:20131017T154000

So far my command does the following:

1) Find .ICS files in directory
2) Only use lines that have DTSTART or DTEND in it
3) Remove the OSX line ending
4) Find lines that don't end with 00

find ~/Library/Calendars -type f -iname *.ics -exec cat -vte {} \; | egrep -ia 'DTSTART;TZID|DTEND;TZID' | tr -d '^M$' | grep -v '00$'

However, I've been unable to find a way to replace the last two digits to 00 and write it directly to the files. Apparently sed '/00$/!s/..$/00/' does some magic but I don't understand the exact syntax and it also doesn't write it to the files!

Any help is much appreciated!

Try this and make sure you have a backup of all your files:

find . -type f -iname *.ics -exec sed -i -e '/^DT\(START\|END\)/s/[0-9][0-9]$/00/' -e 's/\r//' {} \;

if the carriage return is not removed, try changing \r with ^M (ctrl+v+m)

Thanks for your quick reply. Backups are in place. When I run the command I get:

sed: -e: No such file or directory
sed: -e: No such file or directory
sed: -e: No such file or directory
...

Which OS and version of sed do you use?

You could try this:

find . -type f -iname *.ics -exec sed -i '/^DT\(START\|END\)/s/[0-9][0-9]$/00/;s/\r//' {} \;
1 Like

Also test this out first and try:

for file in ~/Library/Calendars/*.calendar/Events/*.ics
do
  [ -f "$file" ] || continue
  sed -i ".bak" -e '/^DTSTART/ba' -e'/^DTEND/ba' -e 'p;d' -e :a -e 's/..\(.\)$/00\1/' "$file"
done
1 Like

Unfortunately I'm forced to do this on OSX Mavericks 10.9 and it looks like the -i option in sed is handled differently - also discussed in this thread: macos - sed -i command for in-place editing to work with both GNU sed and BSD/OSX - Stack Overflow

@Subbeh: I've been running all four variations without an error but the date/time string stays unchanged - I need to do more testing later as I assume it's still the ^M line ending that breaks it..

find . -type f -iname *.ics -exec sed -i "" -e '/^DT\(START\|END\)/s/[0-9][0-9]$/00/' -e 's/\r//' {} \;
find . -type f -iname *.ics -exec sed -i "" -e '/^DT\(START\|END\)/s/[0-9][0-9]$/00/' -e 's/^M//' {} \;
find . -type f -iname *.ics -exec sed -i "" '/^DT\(START\|END\)/s/[0-9][0-9]$/00/;s/\r//' {} \;
find . -type f -iname *.ics -exec sed -i "" '/^DT\(START\|END\)/s/[0-9][0-9]$/00/;s/^M//' {} \;

@Scrutinizer: The loop also works when I remove .bak:

for file in ~/Library/Calendars/*.calendar/Events/*.ics
do
  [ -f "$file" ] || continue
  sed -i "" -e '/^DTSTART/ba' -e'/^DTEND/ba' -e 'p;d' -e :a -e 's/..\(.\)$/00\1/' "$file"
done

Below code will create the filename ".tmp" files with the required changes.

for i in ~/Library/Calendars/*.calendar/Events/*.ics
do
  awk 'BEGIN{FS = OFS = "T"} {if($0 ~ /^DTSTART/ || $0 ~ /^DTEND/) {$NF = $NF - ($NF % 100)}}1' ${i} > ${i}.tmp
done

If you want to make the changes into the file, use below

for i in ~/Library/Calendars/*.calendar/Events/*.ics
do
  awk 'BEGIN{FS = OFS = "T"} {if($0 ~ /^DTSTART/ || $0 ~ /^DTEND/) {$NF = $NF - ($NF % 100)}}1' ${i} > ${i}.tmp && mv ${i}.tmp ${i}
done
1 Like

The code is for BSD sed which is used on OSX. You can use it without .bak , but this is what the man page says:

     -i extension
             Edit files in-place, saving backups with the specified extension.  If a zero-length extension is given, no backup will be
             saved.  It is not recommended to give a zero-length extension when in-place editing files, as you risk corruption or partial
             content in situations where disk space is exhausted, etc.

The input should be a file that ends in '\r\n' and the output is also in this format...

1 Like

Thanks for all the help and explanation! Much appreciated!