선택적 고유 출력을 제공하기 위해 awk의 두 모드 간 텍스트 처리

선택적 고유 출력을 제공하기 위해 awk의 두 모드 간 텍스트 처리

다음 입력 파일이 있습니다.

Policy Name:       KE15-LOCALHOST-APP-RADIX-DAILY

  Policy Type:         Standard
  Active:              yes
  Include:  /appussd
            /home/ussd2ke
            /var/log
            /etc
            /usr

  Schedule:              Montlhy_Full
    Type:                Full Backup
    PFI Recovery:        0
    Maximum MPX:         16
    Retention Level:     5 (3 months)
    Daily Windows:
          Sunday     00:00:00  -->  Sunday     07:00:00
          Monday     00:00:00  -->  Monday     07:00:00
          Tuesday    00:00:00  -->  Tuesday    07:00:00
          Wednesday  00:00:00  -->  Wednesday  07:00:00
          Thursday   00:00:00  -->  Thursday   07:00:00
          Friday     00:00:00  -->  Friday     07:00:00
          Saturday   00:00:00  -->  Saturday   07:00:00

  Schedule:              Weekly_Full
    Type:                Full Backup
    PFI Recovery:        0
    Maximum MPX:         16
    Retention Level:     3 (1 month)
    Daily Windows:
          Wednesday  00:00:00  -->  Wednesday  10:00:00

  Schedule:              Daily_Inc
    Type:                Differential Incremental Backup
    PFI Recovery:        0
    Maximum MPX:         16
    Retention Level:     2 (3 weeks)
    Daily Windows:
          Sunday     01:00:00  -->  Sunday     16:00:00
          Monday     01:00:00  -->  Monday     16:00:00
          Tuesday    01:00:00  -->  Tuesday    16:00:00
          Wednesday  01:00:00  -->  Wednesday  16:00:00
          Thursday   01:00:00  -->  Thursday   16:00:00
          Friday     01:00:00  -->  Friday     16:00:00
          Saturday   01:00:00  -->  Saturday   16:00:00

이제 쉼표와 ;로 구분된 (계획 아래), 보존 수준 및 일일 창 등 다양한 유형 쌍이 필요합니다. 여러 항목의 경우.

내가 시도한 명령은 다음과 같습니다. 문제는 일일 창에 있었습니다. 중간에 데이터를 가져와 일일 창 행을 제거할 수 있었습니다. 이제 요일 이름을 제거하고 고유한 기간만 원합니다.

awk '
  BEGIN { SEP = "" }
  $1 == "Type:" { $1 = ""; T = T SEP $0 }
  $1 == "Retention" && $2 == "Level:" {
    sub(/^.*\(/," ")
    sub(/\).*/,"")
    L = L SEP $0
    if (SEP == "") {
      SEP = ";"
    }
  }
  /Daily Windows:/,/^$/ {
  sub(/^.*Daily.*/,"")
  sub(/^[^A-Z][a-z]+y$/,"")
  S = S SEP $0}
  END {
  sub(/^ */,"",T)
  print T "," L "," S
}'

출력은 다음과 같습니다.

Full Backup; Full Backup; Differential Incremental Backup, 3 months; 1 month; 3 weeks,;;          Sunday     00:00:00  -->  Sunday     07:00:00;          Monday     00:00:00  -->  Monday     07:00:00;          Tuesday    00:00:00  -->  Tuesday    07:00:00;          Wednesday  00:00:00  -->  Wednesday  07:00:00;          Thursday   00:00:00  -->  Thursday   07:00:00;          Friday     00:00:00  -->  Friday     07:00:00;          Saturday   00:00:00  -->  Saturday   07:00:00;;;          Wednesday  00:00:00  -->  Wednesday  10:00:00;;;          Sunday     01:00:00  -->  Sunday     16:00:00;          Monday     01:00:00  -->  Monday     16:00:00;          Tuesday    01:00:00  -->  Tuesday    16:00:00;          Wednesday  01:00:00  -->  Wednesday  16:00:00;          Thursday   01:00:00  -->  Thursday   16:00:00;          Friday     01:00:00  -->  Friday     16:00:00;          Saturday   01:00:00  -->  Saturday   16:00:00

그러나 원하는 출력은 다음과 같습니다.

Full Backup; Full Backup; Differential Incremental Backup, 3 months; 1 month; 3 weeks, 00:00:00  -->  07:00:00; 00:00:00  -->  10:00:00; 01:00:00  -->  16:00:00

답변1

:선택적 콜론 과 FS( )로 최소한 두 개의 공백을 사용하면 FS = ":? *"추가 공백이라는 골치 아픈 문제를 겪지 않고 이 작업에 사용되는 대부분의 주요 필드를 격리할 수 있는 것 같습니다 .

$ cat t20.awk
BEGIN { FS=":?   *"; OFS = ", "; SEP = "; "; }

# if $2 is "Type", append $3 to T
$2 == "Type" { T = (T ? T SEP : "") $3;}

# if $2 is "Retention Level", append sub-string in parenthesis to L
$2 == "Retention Level" && match($0, /\(.*?\)/) {
    L = (L ? L SEP : "") substr($0, RSTART+1, RLENGTH-2)
}

# in Daily window block, skip all line without " --> "
# use an associative array "a" to make sure unique time range
/Daily Windows:/,/^\s*$/ {
    if (!/ --> /) next
    key = $3 " --> " $6
    if (!a[key]++) S = (S ? S SEP : "") key
}

END { print T, L, S }

노트:

  1. 에서는 연결 의 경우 와 유사하게 문자열을 연결할 때 선행 SEP를 피하기 위해 S = (S ? S SEP : "") key트리플을 (S ? S SEP : "")사용합니다 .TL

  2. 에서는 제거 선행을 substr($0, RSTART+1, RLENGTH-2)사용 하고 두 대괄호를 모두 제거합니다.RSTART+1(RLENGTH-2

코드를 실행하세요:

$ awk -f t20.awk file.txt
#Full Backup; Full Backup; Differential Incremental Backup, 3 months; 1 month; 3 weeks, 00:00:00 --> 07:00:00; 00:00:00 --> 10:00:00; 01:00:00 --> 16:00:00

고쳐 쓰다:

의견에 설명된 내용을 바탕으로 Daily Windows코드의 이 부분을 다음과 같이 조정했습니다.

  • 로고를 추가했습니다dw_on시작과 끝을 식별하기 위해일일 창막혔습니다. dw_on == 1이 패턴을 갖고 일치하는 모든 행을 확인해야 합니다. 다음 빈 줄이 감지될 때마다 이 플래그는 다음으로 재설정됩니다./ --> /S0/^\s*$/
  • 변수를 추가했습니다cnt_DW수량을 계산하다일일 창각 일정의 항목입니다. 이는 Daily Windows각 블록이 시작될 때 재설정 됩니다.

해싱(연관 배열)을 통해 고유성이 유지됩니다., 각 블록이 시작될 때 재설정됩니다 Daily Windows. 이 해시의 키는 key = $3 " --> " $6검색하려는 창입니다. 구문: if (!a[key]++) S = (S ? S SEP : "") key아래와 동일

  if (!a[key]) { 
      a[key] = a[key] + 1
      S = (S ? S SEP : "") key 
  }

따라서 (a[key]="") 이전에 본 적이 없는 경우에만 키 key에 a를 추가 할 수 있습니다 S. 두 번째로 이미 존재하는 동일한 키를 처리하면 a[key]==1위의 코드 블록을 건너뜁니다. 이는 awk고유성을 확인하는 일반적인 방법 중 하나입니다.

$ cat t20.1.awk
BEGIN { FS=":?   *"; OFS = ", "; SEP = "; "; }

# if $2 is "Type", append $3 to T
$2 == "Type" { T = (T ? T SEP : "") $3;}

# if $2 is "Retention Level", append sub-string in parenthesis to L
$2 == "Retention Level" && match($0, /\(.*?\)/) {
    L = (L ? L SEP : "") substr($0, RSTART+1, RLENGTH-2)
}

/Daily Windows:/ {
    # turn on the dw_on flag and reset cnt_DW (number of DW entries in a section)
    dw_on = 1; cnt_DW=0;
    # reset the hash 'a' for uniqueness check
    # if you need the uniqueness across all Schedules, then comment it out
    delete a; 
    next;
}

# if dw_on flag is true, i.e. "dw_on == 1"
dw_on {
    # match " --> ", then increase cnt_DW, check the unique window
    # and then append qualified entry to "S"
    if (/ --> /) {
        cnt_DW++
        key = $3 " --> " $6
        if (!a[key]++) S = (S ? S SEP : "") key
    # else if EMPTY line, reset dw_on flag, if cnt_DW is 0, append "No Window" to S
    } else if (/^\s*$/) {
        dw_on = 0;
        if (!cnt_DW) S = (S ? S SEP : "") "No Window"
    }
}

END { 
    # last Schedule section does not have a EMPTY line, so we will need
    # to check up cnt_DW in the last Schedule section in "END" block
    if(dw_on && !cnt_DW) S = (S ? S SEP : "") "No Window";

    # print the result.
    print T, L, S 
}

위 코드를 테스트하기 위해 원본 데이터를 다음과 같이 약간 수정했습니다.

  1. Daily WindowsSchedulePart II에서 별도의 항목을 삭제했습니다 .
  2. Friday 00:00:00 --> Friday 07:00:00첫 번째 일정의 행을 Friday 01:00:00 --> Friday 16:00:00세 번째 일정 섹션의 동일한 행으로 바꿉니다.

따라서 이제 1일정에는 고유창이 2개가 있고, 2일정에는 창이 없고, 3일정에는 고유창이 1개가 있으며, 1일정창과 동일합니다.

위 데이터로 업데이트된 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다.

awk -f t20.1.awk file.txt 
#Full Backup; Full Backup; Differential Incremental Backup, 3 months; 1 month; 3 weeks, 00:00:00 --> 07:00:00; 01:00:00 --> 16:00:00; No Window; 01:00:00 --> 16:00:00

01:00:00 --> 16:00:00서로 다른 타임라인에 있기 때문에 두 개가 있다는 점에 유의하세요 . 마지막 항목을 제거하려면 코드에 표시된 줄을 01:00:00 --> 16:00:00주석 처리하면 다음과 같은 결과를 얻을 수 있습니다.delete a

#Full Backup; Full Backup; Differential Incremental Backup, 3 months; 1 month; 3 weeks, 00:00:00 --> 07:00:00; 01:00:00 --> 16:00:00; No Window

답변2

다음을 통해 모든 작업을 수행할 수 있습니다 awk.

awk -F'(: *|[)(])' '
    /^ *Type/     { type=type==""?$2 : type ";" $2 }
    /^ *Retention/{ Retention=Retention==""?$3 : Retention ";" $3}
    /^ *Wednesday/{ gsub(/ +Wednesday/,"",$0); day=day==""?$0 : day ";" $0}
END{ print type, Retention, day }' OFS=, infile

/ ... /필드 값을 보다 정확하게 일치시키려면 기준 섹션을 조정해야 할 수도 있습니다 .

출력은 예상한 대로입니다.

관련 정보