다음 형식으로 탭으로 구분된 여러 개의 2열 파일을 병합하고 싶습니다.
a
A 5
C 4
D 2
b
A 2
B 5
C 3
c
B 4
C 4
D 2
다음 형식으로 단일 테이블에 넣습니다.
a b c
A 5 2 0
B 0 5 4
C 4 3 4
D 2 0 2
답변1
join
작동하는 도구이지만 옵션이 약간 짜증스럽습니다.
join -t $'\t' -a1 -a2 -o 0,1.2,2.2 file1 file2 |
join -t $'\t' -a1 -a2 -o 0,1.2,1.3,2.2 - file3 |
sed 's/\t\(\t\|$\)/\t0\1/g'
a b c
A 5 2 0
B 0 5 4
C 4 3 4
D 2 0 2
해당 옵션을 먼저 사용했지만 -e
이로 인해 헤더 행에 문제가 발생했습니다.
답변2
본질적으로 값의 2D 배열을 만들고 싶습니다. 각 행의 첫 번째 열은 다음과 같습니다.열쇠, 각 입력 파일의 각 줄에서 탭으로 구분된 첫 번째 필드에서 가져옵니다. 아래의 각 열은 별도의 입력 파일에 해당합니다.
awk 'BEGIN {
RS = "(\r\n|\n\r|\r|\n)"
FS = " *\t *"
SUBSEP = ":"
}
FNR==1 {
++file
}
NF>=2 {
if ($1 in keynum)
key = keynum[$1]
else {
key = ++keys
keynum[$1] = key
keystr[key] = $1
}
value[key,file] = $2
}
END {
files = file
for (key = 1; key <= keys; key++) {
printf "%s", keystr[key]
for (file = 1; file <= files; file++)
printf "\t%s", value[key,file]
printf "\n"
}
}' INPUT1 INPUT2 ... INPUTN
이 BEGIN
규칙은 각 행이 별도의 레코드가 되도록 레코드 구분 기호를 모든 유형의 개행 문자로 설정합니다. 또한 주변의 공백을 포함하여 필드 구분 기호를 탭 문자로 설정합니다.
awk에서 모든 배열은 연관되어 있으며 본질적으로 1차원입니다. 다차원 배열은 인덱스를 연결하여 SUBSEP
지원됩니다 (중간에 하나 포함). 여기서는 :
사용된 인덱스가 양의 정수이기 때문에 구분 기호로 사용합니다. (원하는 경우 tab 과 같은 다른 많은 문자를 사용할 수 있습니다 \t
.)
이 FNR==1
규칙은 각 입력 파일의 첫 번째 줄에서 실행됩니다. 첫 번째 입력 파일, 두 번째 입력 파일 등에 적용 file
되도록 변수를 추가합니다 .1
2
NF>=2
이 규칙은 필드가 두 개 이상 있는 모든 레코드에 대해 트리거됩니다. 이 경우 한 줄에 하나의 탭 문자가 있다는 의미입니다. 첫 번째 필드는열쇠, 두 번째 필드값.
이 변수는 key
고유 키 문자열을 참조하는 양의 정수입니다. (1은 첫 번째 고유한열쇠모든 입력 파일에서 2에서 두 번째 파일까지 표시됩니다. )
연관 배열은 keynum
키 문자열을 키 번호( key
, 양의 정수)에 매핑합니다. 이는 keystr
키 번호를 키 문자열에 매핑하는 역 매핑입니다.
NF>=2
규칙 에서 첫 번째 필드가 이미 알려진 키인 경우 해당 번호를 찾으세요. 그렇지 않으면 첫 번째 필드가 새 고유 키 문자열로 추가됩니다. 그런 다음 두 번째 필드가 value
배열에 저장됩니다.
END
이 규칙은 모든 입력 파일이 처리된 후에 트리거됩니다. 배열 value
은 우리가 원하는 필드를 포함하는 논리적 2D 배열입니다.
외부 루프는 key
처음 표시된 순서대로 표시된 모든 고유 키를 반복합니다. 외부 루프를 반복할 때마다 출력 행이 생성됩니다.
내부 루프는 file
각 입력 파일을 나열된 순서대로 반복합니다. 각 반복은 현재 출력 행에 추가 열을 생성합니다. 각 출력 행에는 지정된 입력 파일 수보다 정확히 하나 더 많은 열이 포함됩니다. (입력 파일이 지정되지 않으면 awk는 표준 입력에서 읽고 이를 입력 파일로 처리합니다.)
이것은 확실히 이를 달성하는 가장 쉬운 방법은 아니지만 강력하기 때문에 이것을 좋아합니다(Unix, Linux, 이전 Mac, 새 Mac, Windows 등 기본적으로 ASCII 호환 문자 집합을 사용하는 모든 곳에서 생성된 입력 파일을 허용합니다. 일부 입력 파일에 알려진 모든 키의 하위 집합만 있으면 혼동될 수 있으며 유사한 상황을 이해하고 유지 관리하고 적응하기가 상대적으로 쉽습니다.
위의 내용을 스크립트로 실행하려면 다음과 같이 저장하세요 paste.awk
.
#!/usr/bin/awk -f
BEGIN {
RS = "(\r\n|\n\r|\r|\n)"
FS = " *\t *"
SUBSEP = ":"
}
FNR==1 {
++file
}
NF>=2 {
if ($1 in keynum)
key = keynum[$1]
else {
key = ++keys
keynum[$1] = key
keystr[key] = $1
}
printf "key = %s, file = %s, value = %s\n", key, file, $2 >/dev/stderr
value[key,file] = $2
}
END {
files = file
for (key = 1; key <= keys; key++) {
printf "%s", keystr[key]
for (file = 1; file <= files; file++)
printf "\t%s", value[key,file]
printf "\n"
}
}
input1
포함 했다면
a
A 5
C 4
D 2
그리고 input2
포함
b
A 2
B 5
C 3
그리고 input3
포함
c
B 4
C 4
D 2
그러나 각 줄의 두 번째 문자는 Tab;
printf ' \ta\nA\t5\nC\t4\nD\t2\n' > input1
printf ' \tb\nA\t2\nB\t5\nC\t3\n' > input2
printf ' \tc\nB\t4\nC\t4\nD\t2\n' > input3
또는 위의 텍스트를 파일에 복사하여 붙여넣은 경우 실행 sed -e 's|^\(.\) *|\1\t|' -i input1 input2 input3
하여 수정한 다음 실행하세요.
paste.awk input1 input2 input3
산출
a b c
A 5 2
C 4 3 4
D 2 2
B 5 4
단지 위의 연속된 공백이 실제로는 s라는 것뿐입니다 tab. 보시다시피 웹사이트의 소프트웨어는 탭을 공백으로 변환합니다.
추가하도록 수정됨: 누락된 항목에 대해 사전 정의된 일부 값을 사용하려면 END
규칙을 다음과 같이 수정하세요.
END {
files = file
for (key = 1; key <= keys; key++) {
printf "%s", keystr[key]
for (file = 1; file <= files; file++)
if ((key SUBSEP file) in value)
printf "\t%s", value[key,file]
else
printf "\t%s", blank
printf "\n"
}
}
blank
원하는 값을 반영하도록 변수를 설정합니다 . (명령줄을 사용하여 설정하거나 awk 코드를 수정하여 규칙의 어딘가 또는 규칙 시작 부분에 값을 설정할 수 있습니다 ./paste.awk -v blank=0 input1 input2 input3
.)BEGIN
END
답변3
이것은 GNU awk 버전입니다. 먼저 빈 값을 0으로 채울 수 있도록 모든 키 값을 찾습니다.
keys=$(cut -d $'\t' -f1 file{1,2,3} | sort -u | paste -sd,)
gawk -F'\t' -v keys="$keys" '
BEGIN {
n = split(keys,k,/,/)
for (i=1; i<=n; i++) values[k[i]] = k[i]
}
{v[$1] = $2}
ENDFILE {
for (key in values)
values[key] = values[key] FS (v[key] ? v[key] : 0)
delete v
}
END {
for (key in values) print values[key]
}
' file1 file2 file3 | sort -t $'\t' -k 1,1