"pattern2"가 마지막으로 나타난 후 처음으로 "pattern1"이 나타나는 줄을 삭제하시겠습니까?

"pattern2"가 마지막으로 나타난 후 처음으로 "pattern1"이 나타나는 줄을 삭제하시겠습니까?

다음과 같은 파일이 있습니다.

블라 블라
패턴 2
블라
패턴 1
패턴 2
블라
블라 패턴 1 블라
블라
패턴 1

pattern1마지막 일치 항목 이후 첫 번째 항목이 포함된 전체 굵은 줄을 제거하고 싶습니다 pattern2.

누구든지 아이디어가 있나요?

감사해요!

답변1

이것은 하나의 ex라이너입니다. ( ex전작과 스크립트 형식에서는 그렇습니다 vi.)

printf '%s\n' '$?pattern2?/pattern1/d' x | ex file.txt

저장 x및 종료. %p변경된 파일만 인쇄하고 싶다면 다음으로 변경하세요.아니요변경 사항을 저장합니다(테스트용으로 좋음).

$는 파일의 마지막 라인을 나타내며, 는 현재 위치에서 시작하여 ?pattern2?역방향 검색의 첫 번째 결과를 나타내는 주소이고, 은 라인 삭제 명령입니다.pattern2/pattern1/d

ex정방향 및 역방향 주소 지정이 필요할 때 사용합니다.


viVim에서도 동일한 작업을 대화식으로 수행할 수 있습니다.

vim file.txt

그런 다음 입력하십시오.

:$?pattern2?/pattern1/d

그리고 Enter를 누르세요.

그런 다음 저장하고 :xEnter를 눌러 종료하십시오.

답변2

여기에는 무차별적인 접근 방식이 있습니다. 데이터를 읽고 두 번 반복합니다. 첫 번째는 패턴 2의 마지막 발생을 검색하고, 두 번째는 패턴 1의 첫 번째 발생을 검색합니다.

#!/usr/bin/perl

# usage:  perl remove-pattern.pl [file]
use strict;

# reads the contents of the text file completely
# removes end of line character and spurious control-M's
sub load {
   my $file = shift;
   open my $in, "<", $file or die "unable to open $file : $!";
   my @file_contents = <$in>;
   foreach ( @file_contents ) { 
      chomp; 
      s/\cM//g; 
   }
   return @file_contents;
}

#  gets the first file from the command line
#  after the perl script
my $ifile = shift;

# read the text file
my @file_contents = &load($ifile);

# set 2 variables for the index into the array 
my $p2 = -1;
my $p1 = -1;

# loop through the file contents and find the last
# of pattern2 (could go reverse the data and find the 
# first of pattern2
for( my $i = 0;$i < @file_contents; ++$i ) {
   if( $file_contents[$i] =~ /pattern2/) {
      $p2 = $i 
   } 
}

# start at the location of the last of pattern2
# and find the first of pattern1
for( my $i = $p2; $i < @file_contents; ++$i ) {
   if($file_contents[$i] =~ /pattern1/) {
     $p1 = $i ;
     last;
   }
}

# create an output file name
my $ofile = $ifile . ".filtered";

# open the output file for writing
open my $out, ">", $ofile or die "unable to open $ofile : $!"; 

# loop through the file contents and don't print the index if it matches
# p1.  print all others
for( my $i = 0;$i < @file_contents; ++$i ) {
   print $out "$file_contents[$i]\n" if ($i != $p1);
}


--- data.txt  ---
bla bla
pattern2
bla
pattern1
pattern2
bla
bla pattern1 bla
bla
pattern1

위의 Perl 스크립트 이름이 "remove-pattern.pl"인 경우 data.txt 입력 파일이 주어지면 다음 명령을 사용하여 실행됩니다. %> perl delete-pattern.pl data.txt

생성된 출력 파일 "data.txt.filtered"

--- data.txt.filtered ---
bla bla
pattern2
bla
pattern1
pattern2
bla
bla
pattern1

답변3

줄의 줄 번호를 찾으려면 다음을 수행하십시오.

lineno=$( nl file | tac | awk '/pattern1/ {last = $1} /pattern2/ {print last; exit}' )

nl파일에 줄 번호를 추가하고,
tac줄을 뒤집고
, awk줄 번호를 인쇄하는 데 사용됩니다.마지막"모드 1"앞으로이것첫 번째"모드 2".

그런 다음 이 줄을 삭제하세요.

sed -i "${lineno}d" file

답변4

파일을 한 번만 전달하고 메모리에 있는 줄 수를 최소화하려는 경우 awk상태 시스템 접근 방식을 사용할 수 있습니다. 이는 가장 짧은 솔루션은 아니지만 쉽게 찾아내고 읽고 유지 관리할 수 있습니다. 상태 이름을 숫자로 대체하여 (잠재적으로) 더 효율적으로 만들 수 있습니다.

PATTERN1=pattern1 PATTERN2=pattern2 awk '
  BEGIN {
    p1 = ENVIRON["PATTERN1"]
    p2 = ENVIRON["PATTERN2"]
    state = "init"
  }
  state == "init" {
    if ($0 ~ p2) state = "p2_found"
    print
    next
  }
  state == "p2_found" {
    if ($0 ~ p1) {
      state = "p1_found"
      p1_line = $0
      printf "%s", hold
      hold = ""
    } else if ($0 ~ p2) {
      # we can print the text held since the last p2
      printf "%s", hold
      hold = $0 RS
    } else hold = hold $0 RS
    next
  }
  state == "p1_found" {
    if ($0 ~ p2) {
      state = "p2_found"
      # the line that matched p1 is not discarded
      printf "%s\n%s", p1_line, hold;
      hold = ""
    }
    hold = hold $0 RS
  }
  END {
    # here we are not printing p1_line which is how it is discarded
    printf "%s", hold
  }'

pattern1( sum과 일치하는 행이 없다고 가정합니다 pattern2.)

관련 정보