使用 bash 解析 ICS 文件
Parsing ICS file with bash
这是一个 google 日历 ics 文件。
我每次都下载,看看有没有新增或更改新的播放事件,我出现在IRC上。
我需要像这样转换文件:
BEGIN:VEVENT
DTSTART:20160612T201000Z
DTEND:20160612T211000Z
DTSTAMP:20160519T200239Z
UID:xxxxxxxxxxxxxxxxxx@google.com
CREATED:20160518T153226Z
DESCRIPTION:
LAST-MODIFIED:20160518T153226Z
LOCATION:OCS Choc
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Ash vs Evil Dead Saison 1 Episode 9 & 10
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=Europe/Brussels:20160611T203500
DTEND;TZID=Europe/Brussels:20160611T233500
DTSTAMP:20160519T202440Z
UID:xxxxxxxx@google.com
RECURRENCE-ID;TZID=Europe/Brussels:20160611T203500
CREATED:20160503T144152Z
DESCRIPTION:
LAST-MODIFIED:20160518T123213Z
LOCATION:RTS Un (Suisse)
SEQUENCE:1
STATUS:CONFIRMED
SUMMARY:The Mysteries Of Laura Saison 2 Episode 1 à 4
TRANSP:TRANSPARENT
END:VEVENT
到
New Events Created :
dim. juin 12 20:10 Ash vs Evil Dead Saison 1 Episode 9 & 10 - OCS Choc
Last Modified Event :
sam. juin 11 20:35 The Mysteries Of Laura Saison 2 Episode 1 à 4 - RTS Un (Suisse)
我需要使用 bash 脚本进行转换。
我必须得到:
DTSTART
已创建
上一次更改
地点
摘要
我需要比较 CREATED 和 LAST-MODIFIED
伪代码:
if (created = LastModified)
then
echo createdevent
else
echo lastModifiedEvent
fi
原生 bash 实现(对于 shell 版本 4.0 或更高版本——旧版本缺少关联数组)如下所示:
#!/bin/bash
handle_event() {
: # put a definition of your intended logic here
}
declare -A content=( ) # define an associative array (aka map, aka hash)
declare -A tzid=( ) # another associative array for timezone info
while IFS=: read -r key value; do
value=${value%$'\r'} # remove DOS newlines
if [[ $key = END && $value = VEVENT ]]; then
handle_event # defining this function is up to you; see suggestion below
content=( )
tzid=( )
else
if [[ $key = *";TZID="* ]]; then
tzid[${key%%";"*}]=${key##*";TZID="}
fi
content[${key%%";"*}]=$value
fi
done
...其中 handle_event
是执行您关心的实际工作的函数。例如,它可能看起来像这样:
local_date() {
local tz=${tzid[]}
local dt=${content[]}
if [[ $dt = *Z ]]; then
tz=UTC
dt=${dt%Z}
fi
shift
if [[ $dt = *T* ]]; then
dt="${dt:0:4}-${dt:4:2}-${dt:6:2}T${dt:9:2}:${dt:11:2}"
else
dt="${dt:0:4}-${dt:4:2}-${dt:6:2}"
fi
# note that this requires GNU date
date --date="TZ=\"$tz\" $dt" "$@"
}
handle_event() {
if [[ "${content[LAST-MODIFIED]}" = "${content[CREATED]}" ]]; then
echo "New Event Created"
else
echo "Modified Event"
fi
printf '%s\t' "$(local_date DTSTART)" "${content[SUMMARY]}" "${content[LOCATION]}"; echo
}
使用给定的输入文件和上述脚本,bash parse-ics <test.ics
产生以下输出(使用我当前的语言环境、时区和语言):
New Event Created
Sun Jun 12 15:10:00 CDT 2016 Ash vs Evil Dead Saison 1 Episode 9 & 10 OCS Choc
Modified Event
Sat Jun 11 15:35:00 CDT 2016 The Mysteries Of Laura Saison 2 Episode 1 à 4 RTS Un (Suisse)
awk
对这样的东西很有用。以下内容可放入新文件(ics.awk):
BEGIN{OFS=" "}
=="DTSTART"{DTSTART=}
=="CREATED"{CREATED=}
=="LAST-MODIFIED"{LASTMODIFIED=}
=="SUMMARY"{SUMMARY=}
=="LOCATION"{LOCATION=}
=="END"{
if (CREATED==LASTMODIFIED)
print "\nNew Event Created"
else
print "\nLast Modified Event"
print DTSTART,SUMMARY,LOCATION
}
你可以这样执行:
awk -F":" -f ics.awk yourfile.ics
用冒号分隔文件中的字段,awk 脚本逐行处理文件。它在找到它时捕获值,然后在找到带有 "END" 的行时打印它们。
上面的脚本会让你关闭:
New Event Created
20160612T201000Z Ash vs Evil Dead Saison 1 Episode 9 & 10 OCS Choc
Last Modified Event
20160612T201000Z The Mysteries Of Laura Saison 2 Episode 1 à 4 RTS Un (Suisse)
使用与 @JNevill 相同的逻辑,但使用关联数组:
ics.awk
BEGIN { FS=":" }
{ a[] = }
== "END" {
printf("%s\n%s %s %s\n\n",
a["CREATED"] == a["LAST-MODIFIED"] ? "New Event Created" : "Last Modified Event",
a["DTSTART"], a["SUMMARY"], a["LOCATION"])
}
然后调用它:
% awk -f ics.awk input-file
New Event Created
20160612T201000Z Ash vs Evil Dead Saison 1 Episode 9 & 10 OCS Choc
Last Modified Event
20160612T201000Z The Mysteries Of Laura Saison 2 Episode 1 Ã 4 RTS Un (Suisse)
但是会留下一个尾随的新行。
这是一个 google 日历 ics 文件。
我每次都下载,看看有没有新增或更改新的播放事件,我出现在IRC上。
我需要像这样转换文件:
BEGIN:VEVENT
DTSTART:20160612T201000Z
DTEND:20160612T211000Z
DTSTAMP:20160519T200239Z
UID:xxxxxxxxxxxxxxxxxx@google.com
CREATED:20160518T153226Z
DESCRIPTION:
LAST-MODIFIED:20160518T153226Z
LOCATION:OCS Choc
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Ash vs Evil Dead Saison 1 Episode 9 & 10
TRANSP:OPAQUE
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=Europe/Brussels:20160611T203500
DTEND;TZID=Europe/Brussels:20160611T233500
DTSTAMP:20160519T202440Z
UID:xxxxxxxx@google.com
RECURRENCE-ID;TZID=Europe/Brussels:20160611T203500
CREATED:20160503T144152Z
DESCRIPTION:
LAST-MODIFIED:20160518T123213Z
LOCATION:RTS Un (Suisse)
SEQUENCE:1
STATUS:CONFIRMED
SUMMARY:The Mysteries Of Laura Saison 2 Episode 1 à 4
TRANSP:TRANSPARENT
END:VEVENT
到
New Events Created :
dim. juin 12 20:10 Ash vs Evil Dead Saison 1 Episode 9 & 10 - OCS Choc
Last Modified Event :
sam. juin 11 20:35 The Mysteries Of Laura Saison 2 Episode 1 à 4 - RTS Un (Suisse)
我需要使用 bash 脚本进行转换。 我必须得到:
DTSTART 已创建 上一次更改 地点 摘要
我需要比较 CREATED 和 LAST-MODIFIED
伪代码:
if (created = LastModified)
then
echo createdevent
else
echo lastModifiedEvent
fi
原生 bash 实现(对于 shell 版本 4.0 或更高版本——旧版本缺少关联数组)如下所示:
#!/bin/bash
handle_event() {
: # put a definition of your intended logic here
}
declare -A content=( ) # define an associative array (aka map, aka hash)
declare -A tzid=( ) # another associative array for timezone info
while IFS=: read -r key value; do
value=${value%$'\r'} # remove DOS newlines
if [[ $key = END && $value = VEVENT ]]; then
handle_event # defining this function is up to you; see suggestion below
content=( )
tzid=( )
else
if [[ $key = *";TZID="* ]]; then
tzid[${key%%";"*}]=${key##*";TZID="}
fi
content[${key%%";"*}]=$value
fi
done
...其中 handle_event
是执行您关心的实际工作的函数。例如,它可能看起来像这样:
local_date() {
local tz=${tzid[]}
local dt=${content[]}
if [[ $dt = *Z ]]; then
tz=UTC
dt=${dt%Z}
fi
shift
if [[ $dt = *T* ]]; then
dt="${dt:0:4}-${dt:4:2}-${dt:6:2}T${dt:9:2}:${dt:11:2}"
else
dt="${dt:0:4}-${dt:4:2}-${dt:6:2}"
fi
# note that this requires GNU date
date --date="TZ=\"$tz\" $dt" "$@"
}
handle_event() {
if [[ "${content[LAST-MODIFIED]}" = "${content[CREATED]}" ]]; then
echo "New Event Created"
else
echo "Modified Event"
fi
printf '%s\t' "$(local_date DTSTART)" "${content[SUMMARY]}" "${content[LOCATION]}"; echo
}
使用给定的输入文件和上述脚本,bash parse-ics <test.ics
产生以下输出(使用我当前的语言环境、时区和语言):
New Event Created
Sun Jun 12 15:10:00 CDT 2016 Ash vs Evil Dead Saison 1 Episode 9 & 10 OCS Choc
Modified Event
Sat Jun 11 15:35:00 CDT 2016 The Mysteries Of Laura Saison 2 Episode 1 à 4 RTS Un (Suisse)
awk
对这样的东西很有用。以下内容可放入新文件(ics.awk):
BEGIN{OFS=" "}
=="DTSTART"{DTSTART=}
=="CREATED"{CREATED=}
=="LAST-MODIFIED"{LASTMODIFIED=}
=="SUMMARY"{SUMMARY=}
=="LOCATION"{LOCATION=}
=="END"{
if (CREATED==LASTMODIFIED)
print "\nNew Event Created"
else
print "\nLast Modified Event"
print DTSTART,SUMMARY,LOCATION
}
你可以这样执行:
awk -F":" -f ics.awk yourfile.ics
用冒号分隔文件中的字段,awk 脚本逐行处理文件。它在找到它时捕获值,然后在找到带有 "END" 的行时打印它们。
上面的脚本会让你关闭:
New Event Created
20160612T201000Z Ash vs Evil Dead Saison 1 Episode 9 & 10 OCS Choc
Last Modified Event
20160612T201000Z The Mysteries Of Laura Saison 2 Episode 1 à 4 RTS Un (Suisse)
使用与 @JNevill 相同的逻辑,但使用关联数组:
ics.awk
BEGIN { FS=":" }
{ a[] = }
== "END" {
printf("%s\n%s %s %s\n\n",
a["CREATED"] == a["LAST-MODIFIED"] ? "New Event Created" : "Last Modified Event",
a["DTSTART"], a["SUMMARY"], a["LOCATION"])
}
然后调用它:
% awk -f ics.awk input-file
New Event Created
20160612T201000Z Ash vs Evil Dead Saison 1 Episode 9 & 10 OCS Choc
Last Modified Event
20160612T201000Z The Mysteries Of Laura Saison 2 Episode 1 Ã 4 RTS Un (Suisse)
但是会留下一个尾随的新行。