file1과 file2 사이의 부분 일치를 기반으로 열을 일치시키고 일치하지 않는 행을 인쇄합니다.

file1과 file2 사이의 부분 일치를 기반으로 열을 일치시키고 일치하지 않는 행을 인쇄합니다.

file1

HIB12  
VH6|KB4  
KB4  
KB4|LKM98|HIB12  

file2

c1 c1 c3 c4 HIB12|LKM98 c6  
c1 c1 c3 c4 KB4|LKM98 c6  
c1 c1 c3 c4 LL15|VH6  
c1 c1 c3 c4 ZZ16|YY15 c6 
c1 c1 c3 c4 ZZ16 c6 
c1 c1 c3 c4 AB1 c6  

두 파일 모두 탭으로 구분됩니다. 파일 1의 열 1은 파일 2의 열 5와 부분적으로 일치합니다. 두 열의 값은 "|"로 구분됩니다. file1의 열 1의 값이 파일 5의 열 5의 값과 일치하면 해당 행은 출력에 인쇄되지 않아야 하며 출력에 일치하지 않는 다른 행이 있어야 합니다. . 시도했지만 예상한 결과를 얻지 못했습니다.

awk 'BEGIN{FS=OFS="\t"} NR==FNR {a[$1]=$5; next} {for (i in a) if (index(i, $5)) print $0, a[i]}' file2 file1

expected output

c1 c1 c3 c4 ZZ16|YY15 c6 
c1 c1 c3 c4 ZZ16 c6  

답변1

GNU awk를 사용하는 다중 문자 RS:

$ awk '
    NR==FNR { a[$0]; next }
    { split($5,v,"|"); for (i in v) if (v[i] in a) next; print }
' FS='\t' RS='[[:space:]|]+' file1 RS='\n' file2
c1 c1 c3 c4 ZZ16|YY15 c6
c1 c1 c3 c4 ZZ16 c6
c1 c1 c3 c4 AB1 c6

또는 awk를 사용하십시오.

$ awk '
    NR==FNR { for (i=1; i<=NF; i++) a[$i]; next }
    { split($5,v,"|"); for (i in v) if (v[i] in a) next; print }
' FS='|' file1 FS='\t' file2
c1 c1 c3 c4 ZZ16|YY15 c6
c1 c1 c3 c4 ZZ16 c6
c1 c1 c3 c4 AB1 c6

답변2

모든 awk와 호환됩니다.

awk 'BEGIN{FS=OFS="\t"}NR==FNR{split($0,vals,"|");for(i in vals){v[vals[i]]}}NR!=FNR{hide=0;for(j in v){if($5~j){hide=1}};if(!hide){print}}' ./file1 ./file2

내 결과는 다음과 같습니다

c1 c1 c3 c4 ZZ16|YY15 c6
c1 c1 c3 c4 ZZ16 c6
c1 c1 c3 c4 AB1 c6

설명하다:

NR==FNR: 첫 번째 파일에서는 동일합니다 NR.FNR

{v[vals[i]]}:값을 허용하지 않는 연관 배열을 만듭니다.

if($5~j){hide=1}: 5번째 필드에 허용되지 않는 값이 있는 경우 숨겨진 행을 설정합니다.

hide=0:새 행의 숨겨진 상태를 재설정합니다.

답변3

$ perl -lane '
    # is this the first file? ($fc is file counter)
    if ($fc == 0) {
      # split first field on pipe chars                         
      my @p = split /\|/, $F[0];
      # use as keys for %patterns hash
      foreach my $p (@p) { $patterns{$p} = 1 };
    } else {
     print unless $F[4] =~ /$regex/;
    };

    if (eof) { # end of file
      if ($fc == 0) { # is this still the first (zeroth) file?
        # use keys of %patterns to build a regular expression
        $regex = join "|", keys %patterns;
      };
      $fc++;
    }' file1 file2
c1      c1      c3      c4      ZZ16|YY15       c6
c1      c1      c3      c4      ZZ16    c6
c1      c1      c3      c4      AB1     c6

그런데, 여기에 중간 변수가 적고 주석이 없는 더 짧은 버전이 있습니다.

perl -lane '
  if ($fc == 0) {
    foreach (split /\|/, $F[0]) { $patterns{$_} = 1 };
  } else {
   print unless $F[4] =~ /$regex/;
  };

  if (eof) {
    $regex = join "|", keys %patterns if ($fc == 0);
    $fc++;
  }' file1 file2

읽을 수 없게 만들고 싶다면 변수 이름을 줄이고 ($c==0)테스트를 더 짧지만 동등한 테스트로 바꾸고(초보자에게는 이해하기 더 어렵기 때문에 보너스입니다!) (!$c)모든 것을 한 줄로 압축하고 추가 공백을 제거하고 반 문자 - 콜론아니요운영 방식을 변경하세요. 어떤 사람들은 이것을 선호합니다 - 마조히즘적인 화물 숭배 FTW!

perl -lane 'if(!$c){foreach(split/\|/,$F[0]){$p{$_}=1}}else{print unless $F[4]=~/$regex/};if(eof){$regex=join"|",keys %p if(!$c);$c++}' file1 file2

답변4

두 번째 파일에서 다섯 번째 필드에는 이미 논리 요소 OR이 포함되어 있습니다.

awk '
NR==FNR {A[$1]; next}
        {for(i in A)
                if(i ~ "^("$5")$") next
        print}
' RS='[\n|]' file1 RS='\n' file2

이제 조건식 줄의 시작과 끝과 괄호의 앵커 포인트만 교체하면 됩니다.

관련 정보