배열에 중복된 인덱스 값이 있습니다.

배열에 중복된 인덱스 값이 있습니다.

다음 문서가 주어지면 :

파일 1:

7997,1
7997,2
7997,3
5114,1
5114,2

파일 2:

7997,52,
5114,12,
4221,52,

file2awk의 데이터와 비교할 값 으로 첫 번째 열을 인덱스로, 두 번째 열을 값으로 사용하여 첫 번째 파일에서 배열을 생성하려면 어떻게 해야 합니까 ?

이 같은:

cat file1 file2 | awk -F, '{if(NF==2){arr[$1]=$2}else{if(arr[$1]){print arr[$1]","$0}}}'

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

1,2,3,7997,52
1,2,5114,12

답변1

한 가지 방법은 다음과 같습니다.

$ awk -F, -vOFS=, 'NR==FNR{a[$1]=a[$1]","$2; next} 
                   ($1 in a){print a[$1],$0}' file1 file2 | 
    sed 's/^,\(.*\),$/\1/'
1,2,3,7997,52
1,2,5114,12

설명하다

  • -F, -vOFS=,: 입력 필드 구분 기호( -F)와 출력 필드 구분 기호( -vOFS런타임에 인쇄되는 각 값 사이에 삽입되는 문자열 print $1,$2)를 쉼표로 설정합니다.

  • NR==FNR{a[$1]=a[$1]","$2; next}: FNR줄 번호입니다현재의파일 NR은 입력 줄 번호입니다. 읽을 두 개의 파일이 주어 지면 awk이러한 변수는 첫 번째 파일을 읽을 때만 동일합니다. 따라서 첫 번째 블록은 NR==FNR{}첫 번째 파일을 읽을 때만 실행됩니다.

    이 블록의 코드는 a첫 번째 필드로 인덱싱된 배열을 만듭니다. 블록이 실행될 때마다 배열의 인덱스에 저장된 항목에 쉼표와 두 번째 필드의 값이 추가됩니다 $1. next첫 번째 파일의 두 번째 블록이 실행되지 않도록 스크립트 실행을 계속하지 않고 다음 입력 줄로 점프합니다 .

    첫 번째 실행은 a[$1]비어 있으므로 배열 시작 부분에 추가 쉼표가 추가됩니다. sed마지막 항목으로 삭제합니다 .

  • ($1 in a){print a[$1],$0}: 이제 두 번째 파일에 있습니다. 행의 첫 번째 필드가 배열의 인덱스인 경우 a현재 행( )의 해당 인덱스와 연관된 값을 인쇄합니다.a$0

  • sed 's/^,\(.*\),$/\1/': 줄의 첫 번째 쉼표( )와 일치하고 괄호를 사용하여 ^,마지막 쉼표( ) \(.*\),$를 제외한 모든 항목을 캡처합니다. 그런 다음 전체 내용을 캡처된 패턴( \1)으로 대체합니다. 결과적으로 각 줄의 첫 번째와 마지막 쉼표만 제거됩니다. 이는 스크립트가 줄 시작 부분에 추가하는 추가 쉼표 awk도 제거하는 것입니다 file2. 원하는 출력에도 표시되지 않기 때문입니다.

답변2

이를 달성하기 위해 FNR및 변수를 사용할 수 있습니다 .NR

awk -F "," '{
  if(FNR==NR){
    if (a[$1] != ""){
      a[$1]=a[$1]","$2
      }
    else{
      a[$1]=$2
      }
    }
    else{
      if (a[$1]!= ""){
        print a[$1]","$1","$2
        }
      }
    }' file1 file2

답변3

에서 시작하다지진P님의 완벽한 답변그리고 논리를 좀 더 강화해 보세요. 이것은 원래 그의 답변에 대한 코멘트였지만, 너무 길어졌습니다(그리고그 자체로 유효한 답변입니다.) 여기 있습니다:

awk 'BEGIN {
  FS = ","
  OFS = ","
}

FNR == NR {
  if ($1 in a) {
    a[$1] = a[$1] OFS $2
  } else {
    a[$1] = $2
  }
  next
}

$1 in a {
  print a[$1], $1, $2
}' file1 file2

일반적으로 말해서, 그렇게 하지 말아야 할 특별한 이유가 없는 한 if ($x in myarray)대신 사용하는 것이 가장 좋습니다 . if (myarray[$x] != "")배열의 요소가 아직 생성되지 않았는지 확인하려면 첫 번째 버전을 사용하세요. 당신이 알고 있다면가지다생성되었으며 빈 문자열이 아닌지 확인하려면 두 번째 문자열을 사용하세요. 두 번째 방법의 비결은 배열 요소에 이름을 지정하는 것만으로 myarray[$x]도 해당 값을 확인하는 상황에서도 자동으로 생성된다는 것입니다. 이로 인해 인쇄 배열을 사용할 때 어떤 경우에는 문제가 발생할 수 있습니다 for (index in myarray).

그리고 를 사용하는 경우 print var1 "," var2 "," var3이는 OFS정확한 사용 사례(출력 필드 구분 기호)입니다. 블록에 OFS를 설정하면 BEGIN전체 스크립트의 출력 형식을 쉽고 빠르게 변경할 수 있습니다.

마지막으로, 첫 번째 파일에서 하나의 작업을 수행하고 두 번째/기타 파일에서 다른 작업을 수행할 때 FNR == NR문으로 끝나는 패턴 블록이 if/else 블록보다 더 깔끔하다고 생각합니다.next

관련 정보