Bash - 다른 파일에 나열된 한 파일의 ID 쌍 찾기

Bash - 다른 파일에 나열된 한 파일의 ID 쌍 찾기

ID 쌍을 나열하는 대용량 파일 "F1"이 있습니다.

id1 = 숫자, id2 = 문자

id1 id2 id1 id2 ...

목표는 두 번째 파일 "F2"에서 다음 형식으로 ID 쌍이 포함된 행을 추출하는 것입니다.

id1 TYHYU 61728497 rtyheyia id2 8372819203948 id1 UJLJF 57383930 utkjruak id2 5683903048377 ...

이를 처리하기 위해 전능한 "grep"을 사용하려고 생각했지만 몇 가지 장애물에 직면했습니다.

F1의 각 Id1 및 Id2는 F2에서 여러 번 반복되므로 더 이상 사용되지 않습니다 grep -Fwf F1.txt F2.txt > F3.txt. 즉, ID1+ID2는 완전한 식별자를 나타냅니다.

실행하면 grep -w "$id1.*$id2" db.txt문제가 해결되지만 어떻게 도달해야 할지 잘 모르겠습니다. (아마도 while read -rgrep을 실행하기 전에 F1의 각 줄을 변수 집합으로 처리하기 위해 어색한 루프를 실행했을 수도 있습니다.)

답변1

거의 다 왔습니다. 이 경우 읽기 루프가 제대로 작동하므로 다음과 같습니다.

while read -r line; do
    id1=$(echo "$line" | cut -d ' ' -f 1)
    id2=$(echo "$line" | cut -d ' ' -f 2)
    grep -w "${id1}.*${id2}" "$F2"
done < "$F1"

그러나 찾고 있는 ID가 데이터의 다른 필드 어디에든 존재하는 경우에는 오탐지가 반환될 수도 있습니다. F2의 ID 필드가 항상 1열과 5열에 표시되도록 보장할 수 있다면 필드 위치도 확인하는 것이 좋습니다. 이 작업은 awk다음 줄을 사용하여 빠르게 수행 할 수 있습니다 grep.

awk -v id1="$id1" -v id2="$id2" '$1 == id1 && $5 == id2 {print $0}' "$F2"

이는 대략적으로 "F2의 각 행에 대해 열 1이 id1이고 열 5가 id2인 경우 전체 행을 인쇄합니다"라고 말합니다.

면책 조항, 나는 이것을 테스트하지 않았습니다.

답변2

@John Moon의 솔루션은 가치가 있으며 이에 투표했습니다. 하지만 귀하의 파일이 "대형"이라고 설명하신 것을 확인했습니다. 기반 솔루션을 사용하려면 awkN 줄이 있는 대용량 파일 f1을 완전히 통과해야 합니다. 그런 다음 큰(?) 파일 f2를 완전히 탐색하는 데 N 번 걸립니다.

그래요아니요전문가 awk. 누군가 f1 파일을 통해 ID를 수집한 다음 f2 파일을 통해 일치 항목을 인쇄할 수 있습니다.

이것은 최고의 솔루션만큼 빠른 grep다소 투박한 솔루션입니다 .awk

먼저 f1.txt 파일(N 줄 길이)을 정규식 파일(역시 N 줄 길이)로 변환합니다.

$ while read id1 id2; do
   printf '^%s[[:blank:]]+' "$id1"
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '%s[[:blank:]]\n' "$id2"
done < f1.txt > regexp.txt

그 추악한 printf 시퀀스는 줄의 시작 부분에서 문자열 일치를 강제한 다음(열 1과 일치하기 위해) 공백을 강제로 생성한 다음 3개의 (공백이 아닌) 문자열 쌍(열 1 무시), 열 2를 반복합니다. , 3 및 4) 그런 다음 열 5의 다른 문자열과 그 뒤에 공백이 있습니다.

따라서 N 행 순회에서 동일한 ID 쌍을 포함 f1.txt하는 행과 일치하는 N 정규식 목록을 만듭니다 . f2.txt목록은 에 저장됩니다 regexp.txt.

f2.txt이제 다음을 사용하여 일치하는 라인을 한 번에 찾을 수 있습니다.

$ egrep -f regexp.txt f2.txt

전체적으로 스크립트는 다음과 같습니다.

$ while read id1 id2; do
   printf '^%s[[:blank:]]+' "$id1"
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '[^[:blank:]]+[[:blank:]]+'
   printf '%s[[:blank:]]\n' "$id2"
done < f1.txt > regexp.txt
$ egrep -f regexp.txt f2.txt

견본:

f1.txt:

id1 id2
id1 id2
id3 id4
id3 id5
id4 id5
id4 id6

f2.txt:

id1 TYHYU 61728497 rtyheyia id2 8372819203948
id1 UJLJF 57383930 utkjruak id2 5683903048377
id1 UJLJF 57383930 utkjruak id2 5683903048377
id3 THREE 4444444 adfhdd id4 182i3746
id2 NOPE 4444444 adfhdd id4 182i3746
id3 TREEE 555555 affff id5 8435987345
id4 FOUR  555055 asdfl id5 3728462
id4 FORE  6666666 dfiuyd id6 845687234

중간 파일 regexp.txt(스크립트로 생성):

^id1[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id2[[:blank:]]
^id1[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id2[[:blank:]]
^id3[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id4[[:blank:]]
^id3[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id5[[:blank:]]
^id4[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id5[[:blank:]]
^id4[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+id6[[:blank:]]

결과 egrep 출력:

$ egrep -f regexp.txt f2.txt 
id1 TYHYU 61728497 rtyheyia id2 8372819203948
id1 UJLJF 57383930 utkjruak id2 5683903048377
id1 UJLJF 57383930 utkjruak id2 5683903048377
id3 THREE 4444444 adfhdd id4 182i3746
id3 TREEE 555555 affff id5 8435987345
id4 FOUR  555055 asdfl id5 3728462
id4 FORE  6666666 dfiuyd id6 845687234

다시 말하지만, 순수한 awk솔루션이 더 빠르고 우아할 수 있습니다. 또한 제가 설명한 접근 방식은 grep패턴 수가 너무 많으면 메모리 부족을 초래할 수 있습니다 . regexp.txt하지만 저는 속도 최적화 기반 솔루션으로 만들겠다고 생각했습니다 grep.

관련 정보