공통 열을 기반으로 여러 파일을 병합하고 파일에 해당 공통 열이 없으면 0을 추가하고 싶습니다. 예를 들어 아래 그림을 참조하세요.
a1.txt
111,222,444,5.5
121,321,555,1.2
a2.txt
111,222,444,7.8
333,321,555,4.5
311,555,222,1.1
a3.txt
333,321,555,9.1
311,555,222,8.8
444,666,777,2.5
일치 항목은 처음 3개 열의 조합이어야 합니다.
출력은 다음과 같아야 합니다.
111,222,444,5.5,7.8,0
121,321,555,1.2,0,0
333,321,555,0,4.5,9.1
311,555,222,0,1.1,8,8
444,666,777,0,0,2.5
3개의 입력 파일 중 4번째 열의 값이 달라서 순서대로 배치하고 싶습니다. a1.txt 값과 마찬가지로 출력 파일의 네 번째 열이 되어야 합니다. a2.txt의 값은 출력 파일의 열 5에 있어야 하고 a3.txt의 값은 출력 파일의 열 6에 있어야 합니다. 아래에서 시도했지만 예상한 결과를 얻지 못했습니다.
awk '{ a[$1 FS $2 FS $3 FS] = a[$1 FS $2 FS $3 FS] ( a[$1 FS $2 FS $3 FS] == "" ? "" : FS) $4 } END{ for (i in a){print i,a[i,0],a[i]} }' FS="," a1.txt a2.txt a3.txt
그래서 4개, 5개, 6개의 입력 파일에 대해 동일한 작업을 수행하고 싶습니다. 누구든지 이 문제를 해결하도록 도와줄 수 있나요?
답변1
awk를 사용하고 출력에서 레코드 순서를 유지하십시오.
awk 'BEGIN{ SUBSEP=OFS=FS="," }
FNR==1 && !reProccss{ fileNr++ }
!reProccss{ keys[$1, $2, $3, fileNr]=$4; next }
reProccss{ key=($1 OFS $2 OFS $3); recNr++
for(i=1; i<=fileNr; i++)
if(seen[key]++<fileNr){
join[key]= join[key] OFS ((key, i) in keys ?keys[key, i]:"0")
data[recNr]= key join[key]
}
}
END{ for(rec=1; rec<=recNr; rec++)
if(data[rec]!="")
print data[rec]
}' a[1-3].txt reProccss=1 a[1-3].txt
또는 join
+shell을 사용하여 여러 열을 키로 단일 키로 변환한 다음 다음 join
과 비슷한 대답으로 명령을 사용합니다.첫 번째 열로 여러 파일 병합(조인은 단일 열을 키로 사용해야만 작동하므로) 원하는 출력을 생성합니다.
-
따라서 처음 두 파일의 특정 문자(예: 입력 파일에 있으면 안 되는 문자)로 구분하여 여러 키 열을 하나의 키 열로 변환한 다음 임시 파일로 출력합니다.joined.tmp
:
join -t, -a1 -a2 -e 0 -o auto \
<(<a1.txt sort |awk -F, -v OFS='-' '{ print $1, $2, $3 FS $4 }') \
<(<a2.txt sort |awk -F, -v OFS='-' '{ print $1, $2, $3 FS $4 }') > joined.tmp
그런 다음 쉘 루프를 사용하여 나머지 파일을 처리합니다.joined.tmp
파일(다음 파일과 연결하기 위해 실행될 때마다 업데이트됨), 루프에서 이미 처리한 처음 두 파일도 건너뜁니다.
for file in ./a*.txt; do
[ "$file" = "./a1.txt" -o "$file" = "./a2.txt" ] && continue
join -t, -a1 -a2 -e 0 -o auto \
joined.tmp <(sort "$file" |awk -F, -v OFS='-' '{ print $1, $2, $3 FS $4 }') >joined.tmp.1
mv joined.tmp.1 joined.tmp
done
마지막으로 추가된 문자를 다시 -
원래 문자 로 변경합니다 ,
.
sed 's/-/,/g' joined.tmp > joined-final.csv
join
입력 파일을 정렬해야 하므로 출력의 레코드 순서가 변경됩니다 .
$ cat joined-final.csv
111,222,444,5.5,7.8,0
121,321,555,1.2,0,0
311,555,222,0,1.1,8.8
333,321,555,0,4.5,9.1
444,666,777,0,0,2.5
답변2
배열 배열 및 ARGIND와 함께 GNU awk 사용:
$ cat tst.awk
BEGIN { FS=OFS=SUBSEP="," }
{ vals[$1,$2,$3][ARGIND] = $NF }
END {
for ( key in vals ) {
printf "%s", key
for ( i=1; i<=ARGIND; i++ ) {
printf "%s%g", OFS, vals[key][i]
}
print ""
}
}
$ awk -f tst.awk *.txt
111,222,444,5.5,7.8,0
311,555,222,0,1.1,8.8
333,321,555,0,4.5,9.1
444,666,777,0,0,2.5
121,321,555,1.2,0,0
출력 라인의 순서가 중요한 경우 조정이 쉽습니다.