이중 일치 - 이전 줄이 다른 패턴과 일치하면 후속 줄의 패턴을 바꾸시겠습니까?

이중 일치 - 이전 줄이 다른 패턴과 일치하면 후속 줄의 패턴을 바꾸시겠습니까?

.ics 파일의 항목이 다른 행의 현재 항목과 일치하는 경우 해당 항목을 바꾸려고 합니다 VEVENT(내보내기가 올바르지 않음).VTODOdate

BEGIN:VCALENDAR
BEGIN:VEVENT
DTSTART:20220340T140000
END:VEVENT
BEGIN:VEVENT
DTSTART:20230620T193700
END:VEVENT
BEGIN:VEVENT
DTSTART:20210210T193800
END:VEVENT
END:VCALENDAR

두 번째 VEVENT 항목에는 현재 시간이 있으므로 다음과 같아야 합니다.

BEGIN:VTODO
DTSTART:20230620T193700
END:VTODO

BEGIN:VEVENT와 행 사이에 더 많은 항목이 있으므로 END:VEVENT명확성을 위해 편집했습니다.

sed로 이것을 시도했지만 범위는 첫 번째 발생이 아닌 전체 파일에서 VEVENT의 첫 번째 발생을 선택합니다.뒤쪽에(또는 이전) 일치하는 패턴이므로 모든 패턴을 대체합니다.

sed -i "/BEGIN:VEVENT/,/DTSTART:$(date +%Y%m%dT%H%M)/{s/VEVENT/VTODO/}" org.ics

나는 이것을 관련이 있다고 생각하는 또 다른 질문에 적용하려고 노력하고 있습니다.문자열을 찾고 첫 번째 문자열을 찾은 후 다른 문자열을 바꿉니다.

sed -n "/DTSTART:$(date +%Y%m%dT%H%M)/,${/END:VEVENT/{x//{x b}g s/VEVENT/VTODO/}}" org.ics

하지만 전혀 작동하지 않습니다: sed: -e expression #1, char 25: unexpected,''

답변1

따라서 다음이 작동합니다.

sed 'H;/BEGIN:VEVENT/h;/END:VEVENT/!d;x;/DTSTART:'"$(date +%Y%m%dT%H%M)"'/s/VEVENT/VTODO/g' org.ics

설명하다:

H: 버퍼(공간)를 생성하기 위해 예약된 공간을 추가한 다음 패턴 매칭을 수행합니다.

/BEGIN:VEVENT/h예약된 공간에 저장하므로 이제 또 다른 패턴 일치를 실행합니다.

/END:VEVENT/!d패턴이 일치하지 않으면 제거됩니다.

x;공간을 유지하기 위해 패턴 공간과 교체하므로 이제 패턴 공간에 필요한 행이 있습니다.

DTSTART... 마지막으로 행이 날짜와 일치하면 행을 바꿉니다. so는 s/.../.../g일치하는 경우에만 실행됩니다.

/DTSTART:'"$(date +%Y%m%dT%H%M)"/s/VEVENT/VTODO/g

고쳐 쓰다

sed '1n;H;/BEGIN:VEVENT/h;/END:VEVENT/!d;x;/DTSTART:'"$(date +%Y%m%dT193700)"'/s/VEVENT/VTODO/g' org.ics | sed '$aEND:CALENDAR

답변2

모든 Unix 시스템의 모든 쉘에서 awk를 강력하게 사용하십시오.

$ cat tst.sh
#!/usr/bin/env bash

awk -v now="$(date +'%Y%m%dT%H%M')" '
    $0 == "BEGIN:VEVENT" {
        eventType = 1
        event = $0
        next
    }
    eventType {
        event = event ORS $0
        if ( $0 == ("DTSTART:" now) ) {
            eventType = 2
        }
        if ( $0 == "END:VEVENT" ) {
            if ( eventType == 2 ) {
                sub(/^BEGIN:VEVENT/,"BEGIN:VTODO",event)
                sub(/END:VEVENT$/,"END:VTODO",event)
            }
            print event
        }
        next
    }
    { print }
' "${@:--}"

$ ./tst.sh file
BEGIN:VCALENDAR
BEGIN:VEVENT
DTSTART:20220340T140000
END:VEVENT
BEGIN:VTODO
DTSTART:20230620T193700
END:VTODO
BEGIN:VEVENT
DTSTART:20210210T193800
END:VEVENT

이것이 실패하는 유일한 방법은 입력에 BEGIN:, END: 또는 DSTART: 줄과 일치하는 줄을 포함할 수 있는 다른 섹션이 있을 수 있지만 이것이 입력에서 작동할지는 의문입니다. 그렇다면, 우리는 그것이 어떻게 지정되는지 보여주어야 합니다.

위 솔루션에서 중요한 점은 입력에 나타날 수 있는 부분 일치뿐만 아니라 전체 텍스트 줄을 대상 문자열과 비교한다는 것입니다.

답변3

해결책은 아니며 힌트만 제공합니다.

sed s/'$'/':'/ org.ics |awk 'BEGIN{RS="BEGIN";FS=":";CD="20220340"} NR>1 {AA[1]="BEGIN";for(i=2;i<=NF;i++){AA[i]=$i;if($i =="DTSTART" && $(i+1) ~ CD ){AA[2]="VTODO"}};for(i in AA){printf AA[i]":"};print ""}'

여기서 sed명령은 각 줄 끝에 :(콜론)을 추가하고 RS="BEGIN"RowSeparator를 BEGIN 키워드로 설정하고(그래서 무시합니다!) FS=":"FieldSeparator를 ":"으로 설정합니다. 각 "행"(키워드 BEGIN에서 다음 BEGIN까지, "레코드"라고 말하는 것이 더 좋음)에서 AA[i]AA[1]은 BEGIN을 무시해야 하므로 필드는 i=2로 시작하는 배열 멤버에 복사됩니다. DTSTART 키워드와 현재 날짜를 모두 확인하고 필요한 경우 VEVENT를 VTODO로 변경합니다. 행을 확인한 후 배열 AA[]가 :를 구분 기호로 사용하여 인쇄됩니다.

이것은 단지 힌트일 뿐이며 테스트해 보지 않았습니다. 직접 디버깅하고 조정해야 합니다. 즉, 필요하지 않은 경우 후행을 지울 수 있습니다.

답변4

우리는 이런 해결책을 가질 수 있습니다TxR:

@(repeat)
@  (cases)
BEGIN:VEVENT
DTSTART:@{date}T@{time}
END:VEVENT
@    (output)
BEGIN:VTODO
DTSTART:@{date}T@{time}
END:VTODO
@    (end)
@  (or)
@line
@    (do (put-line line))
@  (end)
@(end)

이는 그 자체로 모든 항목을 다음에 다시 작성합니다 VTODO.

$ txr  cal.txr cal.ics
BEGIN:VCALENDAR
BEGIN:VTODO
DTSTART:20220340T140000
END:VTODO
BEGIN:VTODO
DTSTART:20230620T193700
END:VTODO
BEGIN:VTODO
DTSTART:20210210T193800
END:VTODO
END:VCALENDAR

date명령줄에서 바인딩 및 변수를 사용할 수 있습니다 .time

$ txr -Ddate=20230620 cal.txr cal.ics
BEGIN:VCALENDAR
BEGIN:VEVENT
DTSTART:20220340T140000
END:VEVENT
BEGIN:VTODO
DTSTART:20230620T193700
END:VTODO
BEGIN:VEVENT
DTSTART:20210210T193800
END:VEVENT
END:VCALENDAR

이제 두 번째 항목만 일치하고 다시 작성됩니다.

예쁜 버전:

Vim 구문 강조

관련 정보