如何修改Bash中的awk命令以支持ICS重复事件转表格?
处理ICS重复事件的awk命令修改方案
原awk脚本仅提取单个VEVENT块中的DTSTART,完全忽略RRULE(重复规则)和EXDATE(排除日期),因此无法展开重复事件。以下是两种可行的解决思路:
方案一:用Python脚本实现完整解析(推荐)
手动用awk解析RRULE的所有规则(如WEEKLY/MONTHLY、UNTIL/COUNT、BYDAY等)和EXDATE会非常繁琐,且容易出错。借助专门的icalendar库可以高效处理所有ICS规范的重复事件:
- 先安装依赖库:
pip install icalendar
- 创建以下Python脚本(命名为
ics_to_table.py):
#!/usr/bin/env python3 from icalendar import Calendar from datetime import datetime, timedelta def process_ics(file_path): with open(file_path, 'rb') as f: cal = Calendar.from_ical(f.read()) for event in cal.walk('VEVENT'): dtstart = event.get('dtstart').dt summary = event.get('summary') rrule = event.get('rrule') exdates = event.get('exdate', []) # 收集所有需要排除的日期 exclude_dates = [] for exd in exdates: exclude_dates.append(exd.dt) if not rrule: # 处理普通事件 print(f"{dtstart.day:02d}/{dtstart.month:02d}/{dtstart.year} {dtstart.hour:02d}:{dtstart.minute:02d} {summary}") continue # 处理重复事件,支持WEEKLY规则(可扩展其他规则) freq = rrule.get('FREQ') if freq == 'WEEKLY': count = rrule.get('COUNT', 52) # 默认生成1年的重复实例 current_date = dtstart for _ in range(count): if current_date not in exclude_dates: print(f"{current_date.day:02d}/{current_date.month:02d}/{current_date.year} {current_date.hour:02d}:{dtstart.minute:02d} {summary}") current_date += timedelta(weeks=1) if __name__ == '__main__': import sys if len(sys.argv) != 2: print(f"用法: {sys.argv[0]} <ICS文件路径>") sys.exit(1) process_ics(sys.argv[1])
- 赋予脚本执行权限并运行:
chmod +x ics_to_table.py ./ics_to_table.py iCal.ics
这个脚本可以轻松扩展支持MONTHLY等其他重复规则,同时自动处理EXDATE排除项,兼容性远优于awk实现。
方案二:简化版awk脚本(仅支持WEEKLY重复)
如果必须使用awk,可以实现一个仅支持WEEKLY规则的简化版本,处理逻辑如下:
BEGIN { FS="[:;]" OFS=" " # 映射BYDAY缩写到星期几数字(0=周日,1=周一...6=周六) day_map["SU"]=0; day_map["MO"]=1; day_map["TU"]=2; day_map["WE"]=3; day_map["TH"]=4; day_map["FR"]=5; day_map["SA"]=6 } /^BEGIN:VEVENT/ { # 重置事件相关变量 dt_epoch=0; summary=""; freq=""; byday=""; count=52; exdates="" in_event=1 } in_event { # 解析DTSTART并转换为Unix时间戳 /^DTSTART/ { year=substr($3,1,4); month=substr($3,5,2); day=substr($3,7,2) hour=substr($3,10,2); minute=substr($3,12,2) dt_epoch=mktime(year " " month " " day " " hour " " minute " 0") } /^SUMMARY/ { summary=$2 } # 解析RRULE的核心参数 /^RRULE/ { for (i=2; i<=NF; i++) { if ($i ~ /^FREQ=/) freq=substr($i,6) if ($i ~ /^BYDAY=/) byday=substr($i,7) if ($i ~ /^COUNT=/) count=substr($i,7)+0 } } # 解析EXDATE并转换为时间戳 /^EXDATE/ { ex_year=substr($3,1,4); ex_month=substr($3,5,2); ex_day=substr($3,7,2) ex_hour=substr($3,10,2); ex_minute=substr($3,12,2) ex_epoch=mktime(ex_year " " ex_month " " ex_day " " ex_hour " " ex_minute " 0") exdates=exdates " " ex_epoch " " } } /^END:VEVENT/ { in_event=0 if (!freq) { # 普通事件按原格式输出 strftime("%d/%m/%Y %H:%M", dt_epoch, out_str) printf("%s %s\n", out_str, summary) } else if (freq == "WEEKLY" && byday in day_map) { # 展开WEEKLY重复事件 current_epoch=dt_epoch for (i=1; i<=count; i++) { if (index(exdates, " " current_epoch " ") == 0) { strftime("%d/%m/%Y %H:%M", current_epoch, out_str) printf("%s %s\n", out_str, summary) } current_epoch += 7*24*60*60 # 增加一周的秒数 } } }
将上述代码保存为ics_parser.awk,运行命令:
awk -f ics_parser.awk iCal.ics
注意:这个awk脚本仅支持WEEKLY重复规则,无法处理UNTIL结束日期、MONTHLY/YEARLY重复等复杂场景,且时区处理依赖系统本地时区,兼容性有限。
内容的提问来源于stack exchange,提问作者aristosv
相关产品推荐
相关产品推荐

