2012-03-29 8 views
4

Ich bin auf der Suche nach einer einfachen/eleganten Möglichkeit, eine Datei so zu grep, dass jede zurückgegebene Zeile mit jeder Zeile einer Musterdatei übereinstimmen muss.Grep, um alle Zeilen der Musterdatei zu entsprechen (perl -e ok auch)

Mit Eingabedatei

acb 
bc 
ca 
bac 

Und Musterdatei

a 
b 
c 

Der Befehl sollte

acb 
bac 

kehre ich versucht, dies mit grep -f zu tun, aber das zurückgibt, wenn es einen einzigen Treffer Muster in der Datei (und nicht alle). Ich probierte auch etwas mit einem rekursiven Aufruf an perl -ne (foreach Zeile der Musterdatei, rufen Sie Perl -ne auf der Suchdatei und versuchen, Grep in Stelle), aber ich konnte nicht den Syntaxparser einen Perl-Aufruf von Perl zu akzeptieren , also nicht sicher, ob das möglich ist.

Ich dachte, es gibt wahrscheinlich einen eleganteren Weg, dies zu tun, also dachte ich, ich würde nachsehen. Vielen Dank!

=== UPDATE ===

Vielen Dank für Ihre Antworten so weit, sorry, wenn ich nicht klar war, aber ich hatte gehofft, für nur eine einzeilige Ergebnis (Erstellen eines Skripts für diese zu schwer scheint, wollte nur etwas schnell). Ich habe einige mehr darüber nachgedacht, und ich kam so weit mit auf den Punkt:

perl -n -e 'chomp($_); print " | grep $_ "' pattern | xargs echo "cat input" 

die

cat input | grep a | grep b | grep c 

Diese Saite druckt ist, was ich ausführen wollen, ich muss nur irgendwie ausführen jetzt. Ich habe versucht, ein zusätzliches Rohr

perl -n -e 'chomp($_); print " | grep $_ "' pattern | xargs echo "cat input" | eval 

eval aber, dass die Nachricht gibt:

xargs: echo: terminated by signal 13 

Ich bin nicht sicher, was das bedeutet?

+0

Signal 13 ist 'SIGPIPE'. Die Pfeife wurde geschlossen, während 'Xargs' darauf schrieb; etwas voraussagbar, denn es gibt nichts in 'eval', das es jemals lesen würde. Ganz und gar nicht klar, was du mit dem Zeug aus den 'Xargs' sowieso erreichen willst. – tripleee

Antwort

3

Eine Möglichkeit, mit perl:

Inhalt des input:

acb 
bc 
ca 
bac 

Inhalt des pattern:

a 
b 
c 

Inhalt des script.pl:

use warnings; 
use strict; 

## Check arguments. 
die qq[Usage: perl $0 <input-file> <pattern-file>\n] unless @ARGV == 2; 

## Open files. 
open my $pattern_fh, qq[<], pop @ARGV or die qq[ERROR: Cannot open pattern file: $!\n]; 
open my $input_fh, qq[<], pop @ARGV or die qq[ERROR: Cannot open input file: $!\n]; 

## Variable to save the regular expression. 
my $str; 

## Read patterns to match, and create a regex, with each string in a positive 
## look-ahead. 
while (<$pattern_fh>) { 
    chomp; 
    $str .= qq[(?=.*$_)]; 
} 

my $regex = qr/$str/; 

## Read each line of data and test if the regex matches. 
while (<$input_fh>) { 
    chomp; 
    printf qq[%s\n], $_ if m/$regex/o; 
} 

Run es mögen:

perl script.pl input pattern 

Mit folgende Ausgabe:

acb 
bac 
0

Nicht grep und nicht ein Liner ...

MFILE=file.txt 
PFILE=patterns 
i=0 
while read line; do 
    let i++ 
    pattern=$(head -$i $PFILE | tail -1) 
    if [[ $line =~ $pattern ]]; then 
    echo $line 
    fi 
    # (or use sed instead of bash regex: 
    # echo $line | sed -n "/$pattern/p" 
done < $MFILE 
+0

Es wäre effizienter, eine innere Schleife über die Linien in der Musterdatei zu haben, als sie bei jeder Iteration von Anfang an neu zu lesen. Außerdem wird dadurch die Eingabezeile mehrmals gedruckt, während die Muster durchlaufen werden. – tripleee

2

Mit Perl, ich schlage vor, Sie alle Muster in ein Array lesen und übersetzen. Dann können Sie Ihre Eingabedatei mit grep lesen, um sicherzustellen, dass alle Regexes übereinstimmen.

Der Code sieht wie folgt aus

use strict; 
use warnings; 

open my $ptn, '<', 'pattern.txt' or die $!; 
my @patterns = map { chomp(my $re = $_); qr/$re/; } grep /\S/, <$ptn>; 

open my $in, '<', 'input.txt' or die $!; 
while (my $line = <$in>) { 
    print $line unless grep { $line !~ $_ } @patterns; 
} 

Ausgang

acb 
bac 
1

Ein anderer Weg, um alle Eingabezeilen zu lesen ist und dann von jedem Muster Filterung starten:

#!/usr/bin/perl 

use strict; 
use warnings; 

open my $in, '<', 'input.txt' or die $!; 
my @matches = <$in>; 
close $in; 

open my $ptn, '<', 'pattern.txt' or die $!; 
for my $pattern (<$ptn>) { 
    chomp($pattern); 
    @matches = grep(/$pattern/, @matches); 
} 
close $ptn; 

print @matches; 

Ausgabe

acb 
bac 
0

A bash (Linux) basierte Lösung

#!/bin/sh 
INPUTFILE=input.txt #Your input file 
PATTERNFILE=patterns.txt # file with patterns 
# replace new line with '|' using awk 
PATTERN=`awk 'NR==1{x=$0;next}NF{x=x"|"$0}END{print x}' "$PATTERNFILE"` 
PATTERNCOUNT=`wc -l <"$PATTERNFILE"` 
# build regex of style :(a|b|c){3,} 
PATTERN="($PATTERN){$PATTERNCOUNT,}" 

egrep "${PATTERN}" "${INPUTFILE}" 
+0

Diese falsch identifiziert "a a a" als Übereinstimmung, wenn Sie "c b a" oder "a b c" wollten. Die Länge allein reicht nicht aus, um dieses Problem richtig zu lösen. – tripleee

0

Hier ist ein grep-only Lösung:

#!/bin/sh 
foo() 
{ 
     FIRST=1 
     cat pattern.txt | while read line; do 
     if [ $FIRST -eq 1 ]; then 
       FIRST=0 
       echo -n "grep \"$line\"" 
     else 
       echo -n "$STRING | grep \"$line\"" 
      fi 
     done 
} 
STRING=`foo` 
eval "cat input.txt | $STRING" 
Verwandte Themen