
Bash 스크립트를 사용하여 일부 비교를 수행하는 효율적인 방법을 찾으려고 합니다. 나는 grep과 awk를 사용하고 있지만 속도가 매우 느리고 일치 항목을 구분할 수 있는 좋은 방법이 없습니다.
입력 파일 1을 고려하십시오.
311 2222
833 7777
Jam 33333
그리고 파일 2를 입력하세요.
A 833 0 0 0 0 7777 0 0 0 0
B no match - 2222 833 3333
C the cow jumps over the 311 moon 2222
D illicit Jam fox 33333
E no match - Jam
F 7777 833
실제로 file1은 약 100줄이고 file2는 약 10,000줄입니다.
file1의 두 문자열을 file2의 각 줄과 비교하고 싶습니다. 두 문자열이 줄의 어느 위치에서나 일치하면 file2에 줄을 인쇄합니다. 위 예의 경우 이상적으로 출력은 다음과 유사합니다(단, 열 형식으로 지정됨).
C the cow jumps over the 311 moon 2222
---<separator>---
A 833 0 0 0 0 7777 0 0 0 0
F 7777 833
---<separator>---
D illicit Jam fox 33333
일치하는 항목은 사이의 구분 기호로 그룹화됩니다(833 7777은 file2의 두 줄과 일치함). 모든 일치는 단어 일치인 것이 좋습니다.
또한 참고: file1의 각 행은 file2의 하나 이상의 행과 일치하지만 file2의 각 행은 file1의 1개 또는 0개 행과 일치합니다.
[file2의 "일치 없음" 줄이 업데이트되었습니다.]
감사해요
답변1
다음은 논리적 AND 연산을 사용하여 패턴 파일의 한 줄당 두 패턴(공백으로 구분)에 대한 순수한 awk 솔루션입니다.
awk 'NR==FNR{patts[$1]=$2;next}{for (i in patts) if (($0 ~ i) && ($0 ~ patts[i])) print}' patterns file
고쳐 쓰다:
정규식 일치가 아닌 단어의 경우 다음 대안을 평가할 수 있습니다.
awk 'NR==FNR{patts[$0]="\\<" $1 "\\>.*\\<" $2 "\\>|\\<" $2 "\\>.*\\<" $1 "\\>";next} \
{for (i in patts) if ($0 ~ patts[i]) print}' patterns file1
실제로 패턴을 변환하여 단어 일치를 833 7777
수행
\<833\>.*\<7777\> | \<7777\>.*\<833\>
하고 두 패턴의 논리적 AND를 시뮬레이션합니다.
이 솔루션은 테스트되었으며 다음 기록과 일치하지 않습니다.G 77771 2833
업데이트 2번
이렇게 하면 구분 기호 문자열을 사용하여 단어 일치, 논리적 AND 및 일치하는 각 그룹의 인쇄가 보장됩니다.
awk 'NR==FNR{patts[$0]="\\<" $1 "\\>.*\\<" $2 "\\>|\\<" $2 "\\>.*\\<" $1 "\\>";next} \
{for (i in patts) {if ($0 ~ patts[i]) !found[i]?found[i]=$0:found[i]=found[i] ORS $0}} \
END{for (k in found) {print found[k];print "-----"}}' patterns file1
#Output
A 833 0 0 0 0 7777 0 0 0 0
F 7777 833
-----
D illicit Jam fox 33333
-----
C the cow jumps over the 311 moon 2222
-----
추신: awk가 연관 배열을 처리하는 방식으로 인해 END 섹션에 있는 배열의 인쇄에 영향을 미칠 수 없습니다. 어떤 면에서는 "무작위"일 것입니다.
답변2
file1의 각 줄에 정확히 두 개의 문자열이 포함되어 있는 것으로 알려진 경우:
while read -ra elements; do
grep "${elements[0]}" file2 | \
grep "${elements[1]}" && \
echo "----"
done < file1
답변3
perl
더 명확해 보인다고 생각해서 이렇게 하겠습니다 .
#!/usr/#bin/env perl
use strict;
use warnings;
use Data::Dumper;
my ( $pattern_file_name, $process_file_name ) = @ARGV;
open ( my $patterns_file, '<', $pattern_file_name ) or die $!;
my @matches = map { [split] } <$patterns_file>;
close ( $patterns_file );
print "Using:\n";
print Dumper \@matches;
#my @matches = ( [ '311', '2222' ], [ '833', '7777' ], [ 'Jam', '33333' ] );
#read main file
my @results;
open ( my $input, '<', $process_file_name ) or die $!;
#iterate a line at a time.
while ( my $line = <$input> ) {
GROUP:
for my $id ( 0 .. $#matches ) {
#Check each set of expressions.
foreach my $expression ( @{ $matches[$id] } ) {
#move to the next group if any don't match
next GROUP unless $line =~ m/$expression/;
}
#didn't get skipped, so must have matched all.
push( @{ $results[$id] }, $line );
}
}
print Dumper \@results;
print "\n$_\n" for @results;
close ( $input );
답변4
perl -wMstrict -Mvars='*f2' -l -0777ane '
if ( ! @ARGV ) {# this is File1 zone: slurped in $_
while ( /^(\S+)\s+(\S+)$/mg ) {
my $rx = qr/^(?=.*$1)(?=.*$2)/m; # AND matching of $1/$2
pos($f2)=0;
$f2 =~ /\G([^\n]+)/m and print $1 while $f2 =~ /$rx/mg;
print "--- <Separator> ---" unless /\G\n\z/;
}
} else {# This is File2 zone: slurped whole in $f2
$f2 = $_;
}
' File2 File1 #<----- order is important here
설명하다:
여기서는 인쇄 순서가 매우 중요하다는 점을 기억해야 합니다. File2의 행은 File1의 문자열에 의해 결정된 순서대로 인쇄됩니다. File1의 각 행이 File1과 패턴 일치된 후에는 일치 성공 여부에 관계없이 별도의 행도 필요합니다. 파일이 빨려지고 File2 => $f2이며 파일은 $_로 처리됩니다.