패턴 앞에 /textline 삽입 - 바로 앞에는 아님

패턴 앞에 /textline 삽입 - 바로 앞에는 아님

송장 파일을 전처리하고 각 송장을 별도의 파일로 분리하려고 합니다. 송장은 여러 페이지로 구성될 수 있습니다. 각 페이지의 제목은 다음과 같습니다.







                                                                 121084
                                                                   A134
                                                               09.17.19
                                                                      1

각 페이지 상단에는 6개의 빈 줄이 있고 그 뒤에 송장 번호, 고객 번호, 날짜, 페이지 번호(그리고 송장의 나머지 부분)가 표시됩니다.

각 페이지의 10번째 줄(각 페이지의 10번째 줄은 송장 페이지 번호)의 "1"(70개의 공백 뒤에 "1"이 옴)마다 1번째 줄에 일부 텍스트를 삽입해야 합니다(구분자로 사용됨). 분할 파일). 송장은 여러 페이지로 구성될 수 있지만 "1"(70개의 공백 뒤에 "1"이 옴)은 새 송장임을 의미합니다.

"1"이 나타나면(70개의 공백 뒤에 "1"이 옴) 그 위의 9줄(이 청구서의 첫 번째 줄) 빈 줄에 텍스트를 삽입하고 싶습니다. 파일의 모든 항목에 대해 이 작업을 수행합니다. 그런 다음 각 송장에 대한 파일을 별도의 파일로 분할할 수 있습니다.

sed를 사용하여 패턴 바로 앞에 데이터를 삽입할 수 있다는 것을 알고 있지만 그 위에 9행 데이터를 어떻게 삽입합니까?

나는 보통 sed와 awk로 그것을 할 수 있지만 이것은 나를 당황하게 합니다.

답변1

당신이 말하고 싶은 것이 다음과 같다고 가정해보자:

여러 송장이 ​​포함된 긴 파일이 있습니다. 모든 청구서는 텍스트로 시작됩니다 70 spaces and a 1. 각 송장 내용 앞에 새 내용 9줄을 삽입해야 합니다.

그러면 10개의 행을 모으기만 하면 됩니다. 현재 줄이 다음과 같을 때송장 시작첫 번째 줄에 새로운 텍스트를 삽입합니다.

실제로 이는 다음과 같이 달성할 수 있습니다.

sed -e '1{N;N;N;N;N;N;N;N}' -e 'N;l;/\n \{70\}1$/{iNew content here' -e 'P};P;D' file

또는 긴 형식(일부 sed 구현에는 주석이 적용되지 않음):

sed '1{                              # (only) on the first line
         N;N;N;N;N;N;N;N             # accumulate 9 lines (first one plus 8 more).
      }
     N                               # On every line,also accumulate that line 
     /\n \{70\}1$/{                  # If buffer ends ($) in 70 spaces and a "1"
i\
New content added here               # insert the content of this line at
                                     # the start of the buffer (10 lines above).
                       P             # and then print it.
                  }
     P;D                             # close the N cycle above by
                                     # printing and deleting one line
    ' file                           # On the selected file.

해결책은 awk송장의 시작을 표시하는 줄에 RS를 설정하는 것입니다. 이 경우 FS를 개행으로 설정하면 각 행이 필드로 분할되고 $(NF-9)는 위의 9번째 행을 참조합니다.

awk -v ln=9 '
              ( NF < ln ){ print; next };           # not enought fields?
                                                    # include
                         { $(NF-ln) = "New Text to include" newln $(NF-ln);
                           print                    # and print
                         };
              BEGIN{ breakln = sprintf("%71s",1);   # 70 spaces and a 1
                     newln   = sprintf("\n");       # a newline
                     RS      = breakln newln;       # set the Record Separator
                     FS      = "\n";                # set the Field separator
                     OFS     = FS;                  # print what got removed
                     ORS     = RS                   # print what got removed
                   }
            ' file

또는 (쉘/awk 솔루션):

breakln="$(printf '%71s' 1)";
newl=$'\n';
awk '  (NF<ln){print;next};
              { $(NF-ln)="New Text to include\n"$(NF-ln); print}' \
    ln=10 \
    RS="$breakln$newl" \
    FS="$newl" \
    OFS="$newl" \
    ORS="$breakln$mewl" \
    file

답변2

awk+ 로 tac:

tac file | awk -v delim="--split page here--" '{
  if (nextnr=="" && $1~/^[0-9]+$/ && $0=="                                                                      "$1) {
     nextnr=NR+9  # pagenr found, remember next position
  }
  else if (NR==nextnr) {
     $0=delim     # overwrite line with delimiter
     nextnr=""    # reset
  }  
  print
}' | tac

먼저 역방향 파일을 사용하여 tac페이지 번호를 위에서 아래로 검색하고 구분 기호를 삽입할 수 있습니다.

이러한 조건이 충족되면 검색은 if 절로 시작됩니다.

  • nextnr(초기) 설정되지 않았습니다( nextnr==""). 다음 구분 기호의 줄 번호를 보유하는 변수입니다.
  • 첫 번째 필드는 숫자( $1~/^[0-9]+$/) 입니다.
  • 줄에는 70개의 공백과 숫자가 포함되어 있습니다.

세 가지 조건이 모두 true인 경우 nextnr현재 행 번호(레코드 수)로 설정됩니다 NR + 9.

현재 줄이 구분 기호( )가 있는 줄인 경우 NR==nextnr해당 줄을 구분 기호로 덮어쓰고 재설정합니다 nextnr.

스크립트의 마지막 줄은 현재 줄(원래 줄 또는 구분 기호로 덮어쓰기)을 인쇄합니다.

마지막 단계에서는 출력이 다시 반전됩니다 tac.

답변3

이것은 스크립트 가능한 편집기 솔루션입니다. 파일의 "(70 공백) 1" 줄 수만을 기준으로 파일에 송장이 몇 개 있는지 알아내는 것이 아이디어입니다. 그러면 스크립트가 여러 번 반복되어 명령을 출력합니다 ed. 루프는 "(70 공백) 1'이 있는 줄 앞의 줄 9"를 점선 절단선으로 변경하는 데 충분한 명령을 출력합니다. -----8<-----선행 마침표( 대체 문자열의 끝을 구별하는 데 사용됨) ed를 제외한 다른 것으로 텍스트를 바꿉니다 . 송장( i < count)을 반복하는 경우 변경 후 10줄 앞으로 이동하여 방금 스크린샷을 찍은 페이지를 재발견하지 않도록 하세요. 루프( i == count)를 완료한 다음 ed의 "write and quit" 명령을 인쇄하면 모든 printf/echo 출력이 파이프로 전송되고 옵션이 ed입력으로 읽혀집니다. -s"자동" 모드 - ed읽거나 쓴 바이트가 보고되지 않습니다.

#!/usr/bin/bash
count=$(grep -c '^                                                                      1' input)
for((i=1; i<=count; i++))
do
  printf '%s\n' '/^                                                                      1/-9c'
  printf '%s\n' '-----8<-----' '.'
  [[ $i < $count ]] && printf '%s\n' '+10'
  [[ $i == $count ]] && echo wq
done | ed -s input

관련 정보