파일의 두 줄을 비교하고 패턴이 일치하면 줄을 삭제합니다.

파일의 두 줄을 비교하고 패턴이 일치하면 줄을 삭제합니다.

이런 파일이 있습니다.

12345 X678GHR 0 ADD
23445 HGT6787 1 ADD
12345 X678GHR 0 REM
67894 OIY5678 0 ADD
12345 OIY5678 0 ADD
12345 X678GHR 1 ADD

나중에 추가하고 삭제한 줄을 제거하려면 파일의 줄을 비교해야 합니다. 따라서 출력은 다음과 같아야 합니다.

23445 HGT6787 1 ADD
67894 OIY5678 0 ADD
12345 OIY5678 0 ADD
12345 X678GHR 1 ADD

이후에 파일에 추가 및 삭제된 기록은 지워집니다.

업데이트: 또한 2열과 3열 사이에 삭제된 레코드가 일치하는지 확인해야 했습니다. 내 원본 파일에서 구분 기호는 공백이 아닙니다. 닫는 괄호 ")"입니다.

도와주세요. 저는 UNIX를 처음 사용합니다.

답변1

항목의 순서를 보장할 필요가 없는 경우 다음을 제공하십시오.

$ cat file
12345)X678GHR)0)ADD
23445)HGT6787)1)ADD
12345)X678GHR)0)REM
67894)OIY5678)0)ADD
12345)OIY5678)0)ADD
12345)X678GHR)1)ADD

다음은

$ awk -F ')' '
    $NF == "ADD" {lines[$1 FS $2 FS $3] = $0} 
    $NF == "REM" {delete lines[$1 FS $2 FS $3]} 
    END {for(i in lines) print lines[i]}
' file
12345)X678GHR)1)ADD
67894)OIY5678)0)ADD
23445)HGT6787)1)ADD
12345)OIY5678)0)ADD

정말로 순서를 유지해야 하는 경우 파일을 두 번 전달하면 됩니다.

$ awk -F ')' '
    NR == FNR {if($NF == "REM") rem[$1 FS $2 FS $3]; next} 
    !($1 FS $2 FS $3 in rem)
' file file
23445)HGT6787)1)ADD
67894)OIY5678)0)ADD
12345)OIY5678)0)ADD
12345)X678GHR)1)ADD

답변2

입력 행의 순서를 유지하는 것이 중요한 경우 단일 연관 배열로는 충분하지 않으므로(awk 및 Perl을 포함한 대부분의 언어에서 연관 배열은 본질적으로 순서가 지정되어 있지 않기 때문에) 두 개의 배열이 필요합니다.

  1. 입력 줄의 텍스트를 포함하는 숫자 인덱스가 있는 배열
  2. 해시와 일치하는 첫 번째 배열의 행 번호를 포함하는 연관 배열입니다.

이는 awk보다 Perl에서 훨씬 쉽기 때문에 이를 사용하겠습니다. @lines첫 번째 배열과 %keys두 번째 배열 에 사용하겠습니다 .

@F이 옵션을 사용할 때 자동으로 생성되는 자동 분할 필드를 포함하는 배열의 이름입니다 -F. $F[0], $F[1]이라는 점을 제외하면 awk의 $1, $2, $3 등과 유사합니다. $F[2 ] 등은 $F[-1]@F의 마지막 요소이며 awk의 와 거의 동일합니다 $NF. Perl 배열은 1이 아닌 0에서 시작합니다.

perl -F'\)' -l -e '
  $key = join(")",@F[0..$#F-1]);
  if ($F[-1] eq "ADD") {
    $lines[$.] = $_;      # $. is the line number of the current file
    $keys{$key} = $.;
  } elsif ($F[-1] eq "REM") {
    delete($lines[$keys{$key}]);
    delete($keys{$key});
  }

  if (eof) {
    foreach $l (@lines) { print $l if $l; };
    @lines = ();
    %keys = ();
  };' inputfile

산출:

23445)HGT6787)1)ADD 
67894)OIY5678)0)ADD
12345)OIY5678)0)ADD
12345)X678GHR)1)ADD

이 버전의 Perl은 입력의 모든 필드에서 작동하며 데이터에 필드 1-3을 사용하고 "ADD" 또는 "REM" 명령에 필드 4를 사용하도록 하드 코딩되지 않았습니다. 대신 데이터의 마지막 필드와 지시문의 마지막 필드를 제외한 모든 필드를 사용합니다. 이는 awk를 사용하여 수행할 수도 있지만 join()마지막 필드를 제외한 모든 필드를 연결하려면 함수나 최소한 간단한 루프를 작성해야 합니다 .

eof()이 Perl 버전은 또한 각 파일의 끝을 감지하고(이 기능을 사용하여) 아직 삭제되지 않은 줄을 인쇄한 후 두 배열을 모두 지워 여러 입력 파일을 처리 할 수 있습니다 . 즉, 각 입력 파일의 끝에서 모든 것을 재설정합니다. @steeldriver의 awk 답변과 같은 블록을 사용할 수도 있었지만 END {}언제 스크립트를 약간 다른 방식으로 재사용하고 싶을지 모르기 때문에 적절한 것 같습니다... 그리고 항상 스스로에게 물어보는 것이 좋습니다. "만약에?" 그리고 "이게 어떻게 실패할 수 있지?"라는 질문을 입력하세요.

답변3

주어진

$ cat input
12345)X678GHR)0)ADD
23445)HGT6787)1)ADD
12345)X678GHR)0)REM
67894)OIY5678)0)ADD
12345)OIY5678)0)ADD
12345)X678GHR)1)ADD

... 그 다음에

$ tac input | perl -n -l -e \
        's/REM$// ? $hit{"${_}ADD"}=1 : print unless delete $hit{$_}' \
    | tac

... 생산하다

23445)HGT6787)1)ADD
67894)OIY5678)0)ADD
12345)OIY5678)0)ADD
12345)X678GHR)1)ADD

설명하다:

  1. tac명령은 파일을 마지막 줄에서 첫 번째 줄로 다시 정렬하므로 일치하는 ADD 줄 앞에 REM 줄이 나타납니다. 다른 하나는 tac마지막에 원래 순서를 복원하는 데 사용됩니다.
  2. Perl 명령 스위치는 다음과 같이 결합될 수 있지만 -nle명확성을 위해 분리되어 있습니다.
    • -n= 각 줄을 자동으로 인쇄하지 않음
    • -l= 각 입력 줄의 끝에서 개행 문자를 제거하고 이를 모든 print명령 에 다시 추가합니다.
    • -e= Perl 스크립트의 한 줄을 지정("입력")합니다.
  3. Perl 코드는 많은 일을 합니다:
    • 연산자 ?:는 if/then/else입니다. condition?expr1:expr2- 조건이 true이면 expr1을 실행하고, 그렇지 않으면 expr2를 실행합니다.
    • 줄 끝에서 "REM"을 성공적으로 제거할 수 있으면(따라서 "12345)X678GHR)0)"과 같은 결과가 나오면 여기에 "ADD"를 추가하고 "히트 목록"에 붙여넣습니다.
    • 그렇지 않은 경우(ADD가 있음을 의미) "히트 목록"에 없는 경우 레코드가 존재하는지 여부에 관계없이 "히트 목록"에서 레코드를 제거합니다.

"Hitlist"는 죽이거나 제거할 항목을 가리키는 구어체 용어입니다. 이 Perl 코드에서는 목록이 아닌 (순서가 지정되지 않은) 해시(일명 연관 배열)의 키로 구현되어 빠른 조회가 가능합니다. 각 키와 관련된 값은 중요하지 않습니다. 여기서는 숫자 1을 사용합니다. (0이나 undef를 제외한 모든 값을 사용할 수 있습니다.)

가정:

  1. 전체 행(모든 열)을 비교하고 있습니다.와는 별개로최종 ADD/REM.
  2. REM 라인을 인쇄하지 않는 것이 허용됩니다(해당 ADD 라인이 없더라도).
  3. 중첩 취소가 발생하지 않습니다. 예를 들어, ADD/ADD/REM/REM(3행이 2행을 취소하고 4행이 1행을 취소함)은 발생하지 않습니다.

관련 정보