6개 파일 간의 공통점 찾기

6개 파일 간의 공통점 찾기

Linux 시스템에는 6개의 .txt 파일이 있으며 각 파일에는 약 1700줄이 있습니다. 이 6개 파일 중 6개, 그 다음 5/6, 4/6, 3/6, 마지막 6개 파일에 공통된 줄을 보고 싶습니다. 다음 코드를 사용해 보았습니다.

comm -12 2.txt 3.txt | comm -12 - 4.txt | comm -12 - 5.txt | comm -12 - 6.txt | comm -12 - 7.txt

그러나 결과가 불완전하고 호환되지 않는 것으로 나타났습니다. 그런 다음 몇 가지 조사를 수행하고 다음 코드를 시도했습니다.

awk 'NR==FNR { lines[$0]=1; next } $0 in lines' 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt

하지만 그것도 작동하지 않습니다.

저는 우분투를 처음 접했기 때문에 아주 기본적인 것만 알고 있습니다. 저를 도와주시면 정말 감사하겠습니다. 미리 감사드립니다.

편집 1:@terdon의 제안에 따라 다음 코드도 시도했습니다.

sort file1 file2 file3 file4 file5 file6 | uniq -c

그것은 나에게 다음과 같은 결과를 제공합니다.

3 VAL455-Main VAL451-Main 
1 VAL455-Main VAL451-Main 
3 VAL463-Main ALA459-Main 
1 VAL463-Main ALA459-Main 
3 VAL468-Main LEU466-Main 
1 VAL468-Main LEU466-Main 
3 VAL468-Main LYS464-Main 

"VAL468-Main LEU466-Main"을 살펴보겠습니다. 보시다시피 출력에는 두 가지 다른 결과가 있습니다. 이는 해당 행이 3개의 파일에 동시에 존재하고 1개의 파일에만 존재한다는 의미입니다. 그러나 이 줄은 모든 파일에 나타납니다.

그래서 우리는 아직 해결책을 찾지 못했습니다.

편집 2:@FelixJN 덕분에 문제의 원인을 찾았습니다. 비슷한 상황에 처해 있다면 그의 답변을 읽어보세요. :)

답변1

함께 awk다음을 수행할 수 있습니다.

#skip if multiple appearance in one file
{if ( seenin[$0] ~ FILENAME ) {next}}
#add filename to list of files the line has been seen in, increase seen counter
{seenin[$0]=seenin[$0]" "FILENAME ; nseen[$0]++}

#print
END {for (line in nseen) { if (nseen[line]>1) {
   printf "%s \"%s\" %s %d %s %s\n",
     "line",line,"seen in",nseen[line],"files:",seenin[line]} } }

제한 사항: 모든 행이 RAM에 보관되므로 메모리입니다.

발생 횟수를 기준으로 정렬하려면 그에 따라 인쇄 명령을 조정해야 합니다(예: 값 기준 정렬) nseen. 쉽습니다 gawk. END블록에 다음 before -loop를 추가하세요 for.

PROCINFO["sorted_in"]="@val_num_desc"

입력 파일:

$ cat file1
a
a
b
b
c
d
e

$ cat file2
c
c
x
z
e
y
z
f

$ cat file3
f
i
a
c
z
i
k

출력( gawk배열 순회 기능 포함 PROCINFO)

$awk -f compare_lines_multifiles.awk file1 file2 file3
line "c" seen in 3 files:  file1 file2 file3
line "z" seen in 2 files:  file2 file3
line "a" seen in 2 files:  file1 file3
line "e" seen in 2 files:  file1 file2
line "f" seen in 2 files:  file2 file3

편집하다:

제공하신 파일은 MSDOS 형식입니다. 변환하여

 dos2unix file1.txt file2.txt ....

또는 에서 레코드 구분 기호를 조정하십시오 awk. 코드의 첫 번째 항목으로 다음을 추가합니다.

 BEGIN { RS="\r\n" }

편집 2: 파일에 불규칙한 구분 기호가 있습니다. 문제는 a<tab>ba<tab>b<tab>가 다른 행으로 처리되는 반면, 동일하다고 생각할 수도 있다는 것입니다.

파일당 두 개의 관심 필드가 있는 특별한 경우에는 전체 행보다는 두 필드의 내용을 비교하는 것이 좋습니다. MSDOS 형식도 고려하십시오.

BEGIN { RS="\r\n" }
#skip if multiple appearance in one file
{if ( seenin[$1"\t"$2] ~ FILENAME ) {next}}
#add filename to list of files the line has been seen in, increase seen counter
{seenin[$1"\t"$2]=seenin[$1"\t"$2]" "FILENAME ; nseen[$1"\t"$2]++}

#print
END {for (line in nseen) { if (nseen[line]>1) {
   printf "%s \"%s\" %s %d %s %s\n",
     "line",line,"seen in",nseen[line],"files:",seenin[line]} } }

결국 6개 파일 모두 더 많이 중복되었습니다. 탭 구분 기호가 있는 두 필드에 초점을 맞추고 한 줄의 출력을 인쇄합니다.

답변2

나는 다른 접근법을 제안하고 싶습니다. 모두 반복하면서 sortuniq -c행이 몇 번이나 표시되는지 계산해 보세요.

sort 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt | uniq -c

그러면 각 줄이 한 번씩 인쇄되지만 해당 줄이 표시된 횟수도 인쇄됩니다. 예를 들어 다음과 같은 세 개의 파일이 있다고 가정해 보겠습니다.

$ cat file1 
dog
cat
bird

$ cat file2
fly
bird
moose

$ cat file3
bird
dog
flea

다음과 같은 결과가 출력됩니다.

$ sort file1 file2 file3 | uniq -c
      3 bird
      1 cat
      2 dog
      1 flea
      1 fly
      1 moose
    

따라서 발견 횟수에 따라 줄을 구분하려면 다음을 수행하여 3개(또는 귀하의 경우 6개) 파일 모두에 나타나는 줄만 볼 수 있습니다.

$ sort file1 file2 file3 | uniq -c | awk '$1==3'
  3 bird
$ sort file1 file2 file3 | uniq -c | awk '$1==2'
      2 dog
$ sort file1 file2 file3 | uniq -c | awk '$1==1'
      1 cat
      1 flea
      1 fly
      1 moose

답변3

첫 번째 시도는 올바른 접근 방식입니다.

comm -12 2.txt 3.txt | comm -12 - 4.txt | comm -12 - 5.txt | comm -12 - 6.txt | comm -12 - 7.txt

이는 작업을 병렬로 완료하는 스트림처럼 작동합니다. 원칙적으로 수백만 줄의 파일을 이런 방식으로 처리할 수 있습니다.

당신이 직면한 문제의사소통(1) 입력 문제, 즉 공백 및 줄 끝으로 인해 발생한 것 같습니다. 이런 것들을 먼저 정리해보면 원래의 방법이 빠르고 편리하다는 것을 알 수 있을 것입니다.

이것을 보여주는 예가 있습니다. 소수 배열로 나눌 수 있는 숫자를 찾으세요.

$ for D in 2 3 5 7 11 13 
> do seq 1 1000 | 
> awk -v D=$D '$0 % D == 0 { print $0 }' | 
> sort > $D
> done

$ comm -12 2 3 | comm -12 - 5 | comm -12 - 7 
210
420
630
840

1부터 1000 사이의 어떤 숫자도 2, 3, 5, 7, 11로 나누어지지 않는다는 사실이 밝혀졌습니다.

답변4

# spits a random three char (a..e)
# string each time it's called
rx() {
  < /dev/urandom \
  tr -cd 'a-e'   |
  fold -w3       |
  head -n 1      \
  ;
}

# modify this section
numFiles=6
numLines=70
commIn=5

# generate random files
for j in $(seq "$numFiles"); do
  for i in $(seq "$numLines"); do
    rx
  done > $j.txt
done

# uniquify each file
# then sort the combined pool
# count n tabulate uniques from this pool
for i in $(seq "$numFiles"); do
  sort -u < "$i.txt"
done                  |
sort | uniq -c        |
sort -b -k1,1nr -k2,2 |
awk -v n="$commIn" '$1+0==n'   ;

이 작업의 결과:

      5 acc
      5 bcd
      5 dac
      5 eea

관련 정보