두 파일을 병합하고 두 파일의 두 번째 열을 추가하고 싶습니다.
파일 1.
001 A
002 B
003 C
004 D
파일 2.
002 D
003 D
005 E
006 F
아래와 같이 파일 3에 병합되어야 합니다.
001 A
002 BD
003 CD
004 D
005 E
006 F
다음 명령을 실행했지만 출력이 올바르지 않습니다.
$ awk 'FNR==NR{a[$1]=$2; next}{print $1, a[$1]$2}' file1 file2
002 BD
003 CD
005 E
006 F
awk 또는 sed를 사용하여 결합하는 방법을 알려주십시오.
답변1
왼쪽/오른쪽 외부 조인 및 전체 외부 조인
사용 중인 명령의 문제점은 file2의 모든 항목이 file1에 있어야 한다는 것입니다. 명령문의 인쇄 부분은 file2의 항목만 평가합니다.
FNR
NR
공통성 기반 JOIN에 이러한 방식으로 자주 사용됩니다. 즉, 한 데이터 세트의 모든 레코드를 선택하고 다른 데이터 세트의 관련 레코드만 선택합니다 . 구체적으로 구현한 것은 "오른쪽" - file2의 모든 항목과 왼쪽의 file1에서 일치하는 멤버를 가져오는 "RIGHT OUTER JOIN"입니다.
대신 "FULL OUTER JOIN"을 수행하려고 합니다. 두 파일의 모든 레코드는 열 1 병합 레코드를 기반으로 합니다.
FNR==NR
"FILE" 레코드 수( FNR
)가 전체 레코드 수( NR
)와 동일함을 나타냅니다. NR
처리된 파일의 각 줄마다 증가하며 FNR
새 파일을 시작할 때 0으로 재설정됩니다. FNR==NR
이는 첫 번째 파일을 읽는 동시에 2개 이상의 파일을 가져오는 경우에만 해당됩니다. awk가 다음 파일로 전환되면 FNR은 0으로 재설정되고 NR은 계속 증가합니다.
이를 설명하기 위해 awk가 입력을 처리할 때 이러한 변수의 상태를 제공하는 print 문을 삽입했습니다.
$> awk 'FNR==NR{a[$1]=$2;printf("File: %s, NR: %s, FNR: %s, $1: %s, $2: %s, a[$1]: %s\n",FILENAME,NR,FNR,$1,$2,a[$1]); next} {printf("File: %s, NR: %s, FNR: %s, $1: %s, $2: %s, a[$1]$2: %s\n",FILENAME,NR,FNR,$1,$2,a[$1]$2); }' file1 file2
File: file1, NR: 1, FNR: 1, $1: 001, $2: A, a[$1]: A
File: file1, NR: 2, FNR: 2, $1: 002, $2: B, a[$1]: B
File: file1, NR: 3, FNR: 3, $1: 003, $2: C, a[$1]: C
File: file1, NR: 4, FNR: 4, $1: 004, $2: D, a[$1]: D
File: file2, NR: 5, FNR: 1, $1: 002, $2: D, a[$1]$2: BD
File: file2, NR: 6, FNR: 2, $1: 003, $2: D, a[$1]$2: CD
File: file2, NR: 7, FNR: 3, $1: 005, $2: E, a[$1]$2: E
File: file2, NR: 8, FNR: 4, $1: 006, $2: F, a[$1]$2: F
해결책
이 문제를 해결하려면 file2를 처리하는 동안 배열에 항목을 계속 추가하고 모든 입력 파일이 처리된 후에 결과만 출력하면 됩니다.
따라서 이 경우 우리는 실제로 전혀 관심을 두지 NR
않습니다 .FNR
모든 입력 파일의 각 텍스트 줄에 대해 첫 번째 열 값을
$1
배열의 인덱스로 사용합니다.a[$1]
열 2의 값을
$2
해당 인덱스의 배열에 할당하되 이미 존재할 수 있는 값을 덮어쓰지 않도록 값을 추가합니다.a[$1]=a[$1]$2
배열을 인쇄하기 전에 모든 레코드/행이 처리될 때까지 기다리십시오.
for (i in a) { printf("%s\t%s\n", i, a[i]) }
유일한 단점은 awk가 정수 대신 문자열 기반 인덱싱을 사용하는 연관 배열을 사용한다는 것입니다(그래서 작동합니다). 그러나 부작용은 이 경우 배열의 항목 순서가 예상과 다를 수 있다는 것입니다. 인쇄된 내용은 숫자 순서(인덱스 기준)가 아니므로 정렬을 위해 출력을 파이프로 연결해야 합니다.
$> awk '{ a[$1]=a[$1]$2; next } END { for (i in a) { printf("%s\t%s\n", i, a[i]) } }' file1 file2 | sort -n
001 A
002 BD
003 CD
004 D
005 E
006 F
대체 방법
Join 명령을 사용하여 이 작업을 수행할 수도 있지만 필드를 결합하는 방법을 모르겠습니다. 여전히 공백으로 구분되어 있으므로 추가 처리 단계가 필요합니다.
$> join -o 0,1.2,2.2 -a1 -a2 file1 file2 | awk '{printf("%s\t%s%s\n", $1, $2, $3)}'
001 A
002 BD
003 CD
004 D
005 E
006 F
해
이는 중복 항목을 거부하는 데 아무런 영향을 미치지 않습니다. 이는 바람직할 수도 있고 원하지 않을 수도 있습니다. 현재 별도의 입력 파일에 중복 레코드가 있으면 병합됩니다.
file1: 001 A
file2: 001 A
출력이 기록됩니다 .
001 AA
답변2
이제 귀하의 연구 결과를 확인했으므로 다음과 같은 해결책을 제시하겠습니다.
awk '{ z[$1]=z[$1]$2 } END { for (i in z) print i, z[i] }' file1 file2
출력은 다음과 같습니다
002 BD
003 CD
004 D
005 E
006 F
001 A
첫 번째 열의 값을 숫자로 정렬하려면 이전 명령의 출력을 다음으로 전달할 수 있습니다 sort
.
awk '{ z[$1]=z[$1]$2 } END { for (i in z) print i, z[i] }' file1 file2 \
| sort -n -k1