我有一个 bash 脚本,它从指定的 ICS (iCalendar) URL 获取日历事件数据并显示当前日期的事件。事件的开始和结束以 ics 格式显示。我不知道如何将 ics 格式转换为 YYYY:MM:DD:HH:mm 格式。
这是我的脚本:
# Define the ICS URL of your calendar
ics_url="your_url"
# Get today's date in YYYYMMDD format
today=$(date +%Y%m%d)
# Use curl to fetch the ICS data and process it
curl -s "$ics_url" | awk -v date="$today" '
BEGIN {
inEvent = 0;
matched = 0;
eventBuffer = "";
}
/BEGIN:VEVENT/ {
inEvent = 1;
matched = 0;
eventBuffer = ""; # Reset event buffer at the beginning of a new event
}
/END:VEVENT/ {
if (matched) {
print eventBuffer; # Print the event buffer if matched
print ""; # Print an empty line after each event
}
inEvent = 0;
}
/^DTSTART:/ {
if (index($0, date) > 0) {
matched = 1;
}
# Replace "DTSTART" with "start"
sub(/^DTSTART:/, "start:", $0);
eventBuffer = eventBuffer "\n" $0;
}
/^DTEND:/ {
# Replace "DTEND" with "end"
sub(/^DTEND:/, "end:", $0);
eventBuffer = eventBuffer "\n" $0;
}
/^SUMMARY|DESCRIPTION/ {
eventBuffer = eventBuffer "\n" $0;
}
'
输出示例:
start:20240502T080000Z
end:20240502T090000Z
SUMMARY:Event
期望的输出:
start:2024:05:02:08:00:00
end:2024:05:02:09:00:00
SUMMARY:Event
或者
start:08:00
end:09:00
SUMMARY:Event
我找到了解决方案。感谢所有的答案。他们提供了很多帮助。
ics_url="your_url"
# Get today's date in YYYYMMDD format
today=$(date +%Y%m%d)
# Use curl to fetch the ICS data and process it
curl -s "$ics_url" | awk -v date="$today" '
BEGIN {
inEvent = 0;
matched = 0;
}
/BEGIN:VEVENT/ {
inEvent = 1;
matched = 0; # Reset the matched flag at the beginning of a new event
start = ""; # Reset variables at the start of a new event
end = "";
summary = "";
}
/END:VEVENT/ {
if (matched) {
# Extract hour and minute from start and end variables
start1 = substr(start, 10, 2);
start2 = substr(start, 12, 2);
end1 = substr(end, 10, 2);
end2 = substr(end, 12, 2);
# Output the data in the specified order
print "Event: " summary;
print "start: " start1 ":" start2;
print "end: " end1 ":" end2;
print ""; # Print an empty line to separate events
}
inEvent = 0;
}
/^DTSTART:/ {
if (index($0, date) > 0) {
matched = 1;
}
# Store the start time without the label
sub(/^DTSTART:/, "", $0);
if (matched) {
start = $0;
}
}
/^DTEND:/ {
# Store the end time without the label
sub(/^DTEND:/, "", $0);
if (matched) {
end = $0;
}
}
/^SUMMARY:/ {
# Store the summary without the label
sub(/^SUMMARY:/, "", $0);
if (matched) {
summary = $0;
}
}'
新输出:
Event: Meeting
start: 10:30
end: 11:30
Event: Go for a walk
start: 10:00
end: 10:30
6
5 个回答
5
假设:
DTSTART / DTEND
可能发生在任何地方,包括外部BEGIN:VEVENT / END:EVENT
但…- 我们只对一对
DTSTART / DTEND
中出现的条目感兴趣BEGIN:VEVENT / END:VEVENT
- 在一
BEGIN:VEVENT / END:VEVENT
对中,4x 所需的数据点可能按任何顺序甚至丢失(如果这不是真的,那么建议的解决方案可能有点矫枉过正,但它仍然应该生成所需的结果)
OP 尚未提供curl
调用的实际输出,因此我将复制/修改 azbarcea 的数据集:
$ cat curl.out
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//iCal4j 1.0//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
DTSTAMP:20240502T120000Z
DTSTART:20240502T090000Z
DTEND:20240505T100000Z
SUMMARY:Team Meeting
DESCRIPTION:Weekly team meeting to discuss progress and upcoming tasks.
LOCATION:Conference Room
UID:1234567890
SEQUENCE:0
END:VEVENT
# scramble order of entries; remove SUMMARY
BEGIN:VEVENT
DESCRIPTION:Present our latest project updates to the client.
DTSTART:20240510T140000Z
DTEND:20240510T160000Z
DTSTAMP:20240502T120000Z
LOCATION:Client's Office
UID:0987654321
SEQUENCE:0
END:VEVENT
END:VCALENDAR
一个awk
想法:
cat curl.out | awk -v date="${today}" '
function parse_dt(dt) { # reformat the ICS datetime string
if (dt ~ /[0-9]{8}T[0-9]{6}Z/)
return substr(dt,1,4) ":" \
substr(dt,5,2) ":" \
substr(dt,7,2) ":" \
substr(dt,10,2) ":" \
substr(dt,12,2) ":" \
substr(dt,14,2)
}
function print_event() {
if (event[1] != "") { # if we have a match (ie, DSTART == date) then print array
print event[1] # event[1] == DTSTART
for (i=2;i<=4;i++) # event[2] == DTEND
if (event[i] != "") # event[3] == SUMMARY
print event[i] # event[4] == DESCRIPTION
}
delete event # clear/reset array
}
BEGIN { FS =":"
map["SUMMARY"] = 3
map["DESCRIPTION"] = 4
}
$2=="VEVENT" { if ($1 == "BEGIN") # we are only interested in DSTART/DTEND pairs within a BEGIN:VEVENT/END:VEVENT pair
inEvent = 1
else
if ($1 == "END" ) {
print_event()
inEvent = 0
}
next
}
inEvent { if ($1 == "DTSTART" && index($2,date) > 0)
event[1] = "start:" parse_dt($2)
else
if ($1 == "DTEND")
event[2] = "end:" parse_dt($2)
else
if ($1 in map) # SUMMARY or DESCRIPTION ?
event[map[$1]] = $0
}
'
注意: OP 将替换cat curl.out |
为curl -s "$ics_curl" |
准备试驾…
因为today=20240502
我们得到:
start:2024:05:02:09:00:00
end:2024:05:05:10:00:00
SUMMARY:Team Meeting
DESCRIPTION:Weekly team meeting to discuss progress and upcoming tasks.
因为today=20240510
我们得到:
start:2024:05:10:14:00:00
end:2024:05:10:16:00:00
DESCRIPTION:Present our latest project updates to the client.
因为today=20240505
我们得到:
-- no ouput
如果这不能满足 OP 的要求,那么 OP 可能需要用更多详细信息更新问题(例如,调用的实际输出curl
、完整的预期输出集)。
|
以下更新应该可以满足您的要求:
# Define the ICS URL of your calendar
ics_url="your_url"
# Get today's date in YYYYMMDD format
today=$(date +%Y%m%d)
# Use curl to fetch the ICS data and process it
curl -s "$ics_url" | awk -v date="$today" '
BEGIN {
inEvent = 0;
matched = 0;
eventBuffer = "";
}
/BEGIN:VEVENT/ {
inEvent = 1;
matched = 0;
eventBuffer = ""; # Reset event buffer at the beginning of a new event
}
/END:VEVENT/ {
if (matched) {
print eventBuffer; # Print the event buffer if matched
print ""; # Print an empty line after each event
}
inEvent = 0;
}
/^DTSTART:/ {
if (index($0, date) > 0) {
matched = 1;
}
# Replace "DTSTART" with "start"
t = mktime(substr($0,9,4) " " substr($0,13,2) " " substr($0,15,2) " " substr($0,18,2) " " substr($0,20,2) " " substr($0,22,2))
line = "start:" strftime("%Y:%m:%d:%H:%M:%S",t)
eventBuffer = eventBuffer "\nstart:" line;
}
/^DTEND:/ {
# Replace "DTEND" with "end"
t = mktime(substr($0,7,4) " " substr($0,11,2) " " substr($0,13,2) " " substr($0,16,2) " " substr($0,18,2) " " substr($0,20,2))
line = strftime("%Y:%m:%d:%H:%M:%S",t)
eventBuffer = eventBuffer "\nend:" line;
}
/^SUMMARY|DESCRIPTION/ {
eventBuffer = eventBuffer "\n" $0;
}
'
使用以下内容进行测试input
:
$ cat input.txt
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//iCal4j 1.0//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
DTSTAMP:20240502T120000Z
DTSTART:20240505T090000Z
DTEND:20240505T100000Z
SUMMARY:Team Meeting
DESCRIPTION:Weekly team meeting to discuss progress and upcoming tasks.
LOCATION:Conference Room
UID:1234567890
SEQUENCE:0
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20240502T120000Z
DTSTART:20240510T140000Z
DTEND:20240510T160000Z
SUMMARY:Client Presentation
DESCRIPTION:Present our latest project updates to the client.
LOCATION:Client's Office
UID:0987654321
SEQUENCE:0
END:VEVENT
END:VCALENDAR
输出将是:
start:start:2024:05:05:09:00:00
end:2024:05:05:10:00:00
SUMMARY:Team Meeting
DESCRIPTION:Weekly team meeting to discuss progress and upcoming tasks.
start:start:2024:05:10:14:00:00
end:2024:05:10:16:00:00
SUMMARY:Client Presentation
DESCRIPTION:Present our latest project updates to the client.
我运行它有点不同……我的输入为input.txt
,awk
脚本为format.awk
:
BEGIN {
inEvent = 0;
matched = 0;
eventBuffer = "";
}
/BEGIN:VEVENT/ {
inEvent = 1;
matched = 0;
eventBuffer = ""; # Reset event buffer at the beginning of a new event
}
/END:VEVENT/ {
if (matched) {
print eventBuffer; # Print the event buffer if matched
print ""; # Print an empty line after each event
}
inEvent = 0;
}
/^DTSTART:/ {
if (index($0, date) > 0) {
matched = 1;
}
# Replace "DTSTART" with "start"
t = mktime(substr($0,9,4) " " substr($0,13,2) " " substr($0,15,2) " " substr($0,18,2) " " substr($0,20,2) " " substr($0,22,2))
line = "start:" strftime("%Y:%m:%d:%H:%M:%S",t)
eventBuffer = eventBuffer "\nstart:" line;
}
/^DTEND:/ {
# Replace "DTEND" with "end"
t = mktime(substr($0,7,4) " " substr($0,11,2) " " substr($0,13,2) " " substr($0,16,2) " " substr($0,18,2) " " substr($0,20,2))
line = strftime("%Y:%m:%d:%H:%M:%S",t)
eventBuffer = eventBuffer "\nend:" line;
}
/^SUMMARY|DESCRIPTION/ {
eventBuffer = eventBuffer "\n" $0;
}
并使用 CLI:
awk -f format.awk input.txt
5
-
2第一个脚本的输出似乎显示了如果
today
未设置 (bash) 变量会发生什么(因此您能够显示 2 个不同日期的事件 – 20240505 和 20240510);调用第二个脚本 (awk -f format.awk input.txt
) 缺少该-v date="${today}"
子句;这些mktime()/strftime()
调用并不是真正需要的…您可以直接填充各种调用eventBuffer
的结果substr()
–
-
真的。真的。真的。我
${today}
在测试中没有使用。我曾经mktime
举例说明,如果格式需要扩展为其他内容。
– -
您应该提到,时间函数(mktime 和 strftime)需要 GNU awk。
– -
对我来说没有 1:1 效果,因为 MacOS 不支持 Gawk(我可能应该提到这一点)。但我最终使用了 substr。谢谢您的回答!
–
-
2@MoritzSchäfer MacOS 确实支持 gawk,只是默认情况下不支持 gawk。您可以轻松安装它,如果您将来需要使用 awk 进行其他项目,它将为您节省大量时间和精力。
–
|
每当你的输入中有标签值对时,就像在这种情况下一样,我发现最好首先创建一个数组将标签(例如"DTSTART"
)映射到它们的关联值(v[]
如下),然后你可以比较、打印、修改你喜欢的任何值只需通过适当的标签对数组进行索引即可。使用任何 POSIX awk:
$ cat tst.sh
#!/usr/bin/env bash
today=$(date +%Y%m%d)
# replace this `cat` with your `curl` command
cat input.txt |
awk -v date="$today" '
{
if ( match($0,/^[[:upper:]]+:/) ) {
tag = substr($0,RSTART,RLENGTH-1)
v[tag] = substr($0,RSTART+RLENGTH)
}
else {
v[tag] = v[tag] ORS $0
}
}
/^END:VEVENT/ {
if ( v["DTSTART"]+0 == date ) {
print "start:" dfmt(v["DTSTART"])
print "end:" dfmt(v["DTEND"])
print "SUMMARY:" v["SUMMARY"] ORS v["DESCRIPTION"] ORS
}
delete v
}
function dfmt(date) { return \
substr(date,10,2) ":" \
substr(date,12,2)
}
'
即使您的输入包含多行DESCRIPTION
或任何其他字段(假设它符合提供的输入的修改版本:
$ cat input.txt
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//iCal4j 1.0//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
DTSTAMP:20240502T120000Z
DTSTART:20240503T090000Z
DTEND:20240505T100000Z
SUMMARY:Team Meeting
DESCRIPTION:Weekly team meeting to
discuss progress and
upcoming tasks.
LOCATION:Conference Room
UID:1234567890
SEQUENCE:0
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20240502T120000Z
DTSTART:20240510T140000Z
DTEND:20240510T160000Z
SUMMARY:Client
Presentation
DESCRIPTION:
Present our latest project
updates to the client.
LOCATION:Client's Office
UID:0987654321
SEQUENCE:0
END:VEVENT
END:VCALENDAR
我们会得到这样的输出:
$ ./tst.sh
start:09:00
end:10:00
SUMMARY:Team Meeting
Weekly team meeting to
discuss progress and
upcoming tasks.
显然,您可以调整它以调整空白和/或输出日期格式,但是您喜欢,我将 和SUMMARY
部分组合DESCRIPTION
在一个输出块中,因为这就是问题中的代码的作用,但当然您可以单独打印它们。
关于问题中的脚本:
- 你永远不需要做
/regexp/ { sub(/regexp/, string); whatever }
,你可以做sub(/regexp/, string) { whatever }
以避免重复,/regexp/
因为在这两种情况下,sub()
只会做一些事情,并且只有在匹配时才会执行/regexp/
。这适用于sub()s
您的脚本中的两者 - 您可以删除整个
BEGIN
部分,因为 awk 会自动将这些变量初始化为零或 null,因此手动执行此操作不会为您的脚本添加任何功能。 - 您应该在每个部分中使用
ORS
而不是硬编码,以便这些部分将使用脚本其余部分使用的任何设置,而不是希望/假设将是。"\n"
eventBuffer = eventBuffer "\n" $0
ORS
ORS
\n
|
我将AWK
按照以下方式利用 GNU 来完成这项任务,让file.txt
内容成为
start:20240502T080000Z
end:20240502T090000Z
SUMMARY:Event
然后
awk 'BEGIN{FS=OFS=":"}$1=="start"||$1=="end"{gsub(/[TZ]/,"");$0=gensub(/([0-9][0-9])/,":\\1","g");$0=gensub(/::([0-9][0-9]):/,":\\1",1)}{print}' file.txt
给出输出
start:2024:05:02:08:00:00
end:2024:05:02:09:00:00
SUMMARY:Event
说明:我通知 GNUAWK
这:
既是字段分隔符 ( FS
) 又是输出字段分隔符 ( OFS
)。当遇到start
或end
在第一个字段中时,我删除所有T
并Z
使用 gsub,然后使用为每对数字添加前缀:
,然后再次使用它删除:
第一个(多余)和第二个(年内)之前的内容。
|
我还添加了一个选项,将 UTC +0 时间转换为不同的时区(在我的例子中为 +2 UTC)。
timeshift="2"
start1 = substr(start, 10, 2);
start2 = substr(start, 12, 2);
start1= start1+timeshift;
end1 = substr(end, 10, 2);
end2 = substr(end, 12, 2);
end1= end1+timeshift;
|
curl -s "$ics_url"
;还更新问题以显示预期结果;如果curl
输出包含“太多”条目,则删除除几个条目之外的所有条目(一个符合您的要求,一个不符合您的要求)–
substr
一个冗长但简单的方法–
^SUMMARY|DESCRIPTION
可能与您的意图不符–
–
inEvent
但从未引用它;可以DTSTART
并且DTEND
发生在一BEGIN:VEVENT / END:VEVENT
对之外吗?–
|