grep für mehrere Zeichenfolgen in einer Datei in verschiedenen Zeilen (dh ganze Datei, keine zeilenbasierte Suche)?


84

Ich möchte nach Dateien suchen, die die Wörter enthalten Dansk, Svenskaoder Norskin einer beliebigen Zeile mit einem verwendbaren Rückkehrcode (da ich wirklich nur die Information haben möchte, dass die Zeichenfolgen enthalten sind, geht mein Einzeiler etwas weiter als dies).

Ich habe viele Dateien mit Zeilen wie diesen:

Disc Title: unknown
Title: 01, Length: 01:33:37.000 Chapters: 33, Cells: 31, Audio streams: 04, Subpictures: 20
        Subtitle: 01, Language: ar - Arabic, Content: Undefined, Stream id: 0x20, 
        Subtitle: 02, Language: bg - Bulgarian, Content: Undefined, Stream id: 0x21, 
        Subtitle: 03, Language: cs - Czech, Content: Undefined, Stream id: 0x22, 
        Subtitle: 04, Language: da - Dansk, Content: Undefined, Stream id: 0x23, 
        Subtitle: 05, Language: de - Deutsch, Content: Undefined, Stream id: 0x24, 
(...)

Hier ist der Pseudocode von dem, was ich will:

for all files in directory;
 if file contains "Dansk" AND "Norsk" AND "Svenska" then
 then echo the filename
end

Was ist der beste Weg, dies zu tun? Kann es in einer Zeile gemacht werden?

Antworten:


89

Sie können verwenden:

grep -l Dansk * | xargs grep -l Norsk | xargs grep -l Svenska

Wenn Sie auch in versteckten Dateien finden möchten:

grep -l Dansk .* | xargs grep -l Norsk | xargs grep -l Svenska

Clevere Lösung; Eine Sache zu beachten (allgemein gesprochen, nicht relevant, was die OP für bittet) ist , dass der Gesamt Exit - Code wird 0 auch bei (konzeptionellem) Versagen. Wenn Sie also daran interessiert sind, Fehler oder Erfolg zu bestimmen, müssen Sie entweder prüfen, ob die Standardausgabe leer ist oder nicht, oder stattdessen den Ansatz von @ EddSteel verwenden.
mklement0

@mklement: In Bash PIPESTATUSenthält das Array die Exit-Werte der Mitglieder einer Pipeline.
Bis auf weiteres angehalten.

@ TennisWilliamson Das ist gut zu wissen, danke. Eine andere Option ist, die pipefailShell-Option (vorübergehend) shopt -so pipefail
einzuschalten

4
Möglicherweise möchten Sie verwenden grep -Zund xargs -0ob Ihre Dateinamen Leerzeichen enthalten können.
Ben Challenor

1
Dies kann zu "Argumentliste zu lang" -Fehlern führen, wenn Sie viele Dateien haben.
AnnanFay

23

Noch eine andere Möglichkeit, nur Bash und Grep zu verwenden:

Für eine einzelne Datei 'test.txt':

  grep -q Dansk test.txt && grep -q Norsk test.txt && grep -l Svenska test.txt

Wird gedruckt, test.txtwenn die Datei alle drei enthält (in beliebiger Kombination). Die ersten beiden Greps drucken nichts ( -q) und die letzten drucken die Datei nur, wenn die anderen beiden bestanden haben.

Wenn Sie dies für jede Datei im Verzeichnis tun möchten:

   für f in *; mache grep -q Dansk $ f && grep -q Norsk $ f && grep -l Svenska $ f; getan

Aber dann muss grep nicht dreimal ausgeführt werden.
Kurumi

1
Ich weiß, dass Sie Muster mit -e kombinieren können, aber ich konnte keinen Weg finden, eine Konjunktion allein in grep herzustellen.
Edd Steel

1
Toll; re for f ...: use "$f"(doppelte Anführungszeichen) anstatt nur $fsicherzustellen, dass Dateinamen mit eingebetteten Leerzeichen usw. korrekt behandelt werden.
mklement0

Der Vorteil dieses Ansatzes gegenüber @ vmpstr besteht darin, dass der Exit-Code korrekt wiedergibt, ob alle Suchbegriffe gefunden wurden oder nicht.
mklement0

19
grep irl word1 * | grep il word2 `cat -` | grep il word3 `cat -`
  • -i macht die Groß- und Kleinschreibung unabhängig
  • -r macht die Dateisuche durch Ordner rekursiv
  • -l leitet die Liste der Dateien mit dem gefundenen Wort weiter
  • cat - bewirkt, dass der nächste grep die an die Liste übergebenen Dateien durchsucht.

1
Dies ist die einfachste und direkteste Antwort, sehr hilfreich, danke!
Majick

9

So suchen Sie nach mehreren Zeichenfolgen in einer Datei in verschiedenen Zeilen (Verwenden Sie das Pipe-Symbol):

for file in *;do 
   test $(grep -E 'Dansk|Norsk|Svenska' $file | wc -l) -ge 3 && echo $file
done

Anmerkungen:

  1. Wenn Sie ""mit Ihrem grep doppelte Anführungszeichen verwenden , müssen Sie der Pipe wie folgt entkommen: \|um nach Dansk, Norsk und Svenska zu suchen.

  2. Angenommen, eine Zeile hat nur eine Sprache.

Exemplarische Vorgehensweise: http://www.cyberciti.biz/faq/howto-use-grep-command-in-linux-unix/


Würde das nicht scheitern, wenn Dansk Norsk und Svenska alle in derselben Zeile erscheinen würden?
vmpstr

Ja. In diesem Fall würde es scheitern. Ich nahm an, dass die Sprachen eine pro Zeile erscheinen.
Damodharan R

Es würde auch archivieren, wenn ich nur hätte Norsk, aber in drei verschiedenen Zeilen.
Benjamin W.

5

Mit ack geht das ganz einfach :

ack -l 'cats' | ack -xl 'dogs'
  • -l: Gibt eine Liste der Dateien zurück
  • -x: Nehmen Sie die Dateien von STDIN (der vorherigen Suche) und durchsuchen Sie nur diese Dateien

Und Sie können einfach so lange weiterleiten, bis Sie genau die gewünschten Dateien erhalten.


Wenn ich das versuche, heißt es Unknown option: x. Gibt es eine bestimmte Version von ack, die dieses x-Flag unterstützt?
Hassan

5

Dadurch werden mehrere Wörter in mehreren Dateien gesucht:

egrep 'abc|xyz' file1 file2 ..filen 

2
Dies findet nicht nur Dateien mit beiden Zeichenfolgen, sondern auch Dateien mit 'abc' ODER 'xyz'. Ich denke, OP hat nach Dateien gefragt, die 'abc' UND 'xyz' enthalten.
Chris Warth

4
awk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print "0" }' 

Sie können dann den Rückgabewert mit der Shell abfangen

wenn Sie Ruby haben (1.9+)

ruby -0777 -ne 'print if /Dansk/ and /Norsk/ and /Svenka/' file

1
in Ihrer awk END-Klausel möchten Sie wahrscheinlich: if (a && b && c) {exit 0} else {exit 1}oder knapperexit !(a && b && c)
Glenn Jackman

Ihre Rubinlösung sieht nicht richtig aus. Dadurch werden nur Absätze gedruckt, die alle Suchwörter enthalten. Die Frage ist: Enthält die Datei (als Ganzes) alle Wörter, auch wenn sie nicht alle im selben Absatz erscheinen?
Glenn Jackman

Vielen Dank. geändert, wenn die gesamte Datei benötigt wird, dann muss -0777
kurumi

3

Einfach:

grep 'word1\|word2\|word3' *

Weitere Informationen finden Sie in diesem Beitrag


Ich würde die -lFlagge hinzufügen , aber ansonsten scheint mir diese Antwort am einfachsten zu sein, es sei denn, ich vermisse etwas.
xdhmoore

Ja, es ist auch effizienter, da Sie nicht alle Daten in mehreren
Rohren

3
Die Frage fragt nach einem Ausdruck, der Dateien zurückgibt, die alle drei Begriffe enthalten. Dies gibt Zeilen (anstelle von Dateinamen) zurück, die eine der drei (anstelle aller drei) enthalten.
Benjamin W.

2

Dies ist eine Mischung aus den Antworten von Glenn Jackman und Kurumi, die eine beliebige Anzahl von Regexen anstelle einer beliebigen Anzahl von festen Wörtern oder einer festen Menge von Regexen zulässt.

#!/usr/bin/awk -f
# by Dennis Williamson - 2011-01-25

BEGIN {
    for (i=ARGC-2; i>=1; i--) {
        patterns[ARGV[i]] = 0;
        delete ARGV[i];
    }
}

{
    for (p in patterns)
        if ($0 ~ p)
            matches[p] = 1
            # print    # the matching line could be printed
}

END {
    for (p in patterns) {
        if (matches[p] != 1)
            exit 1
    }
}

Führen Sie es so aus:

./multigrep.awk Dansk Norsk Svenska 'Language: .. - A.*c' dvdfile.dat

2

Folgendes hat bei mir gut funktioniert:

find . -path '*/.svn' -prune -o -type f -exec gawk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print FILENAME }' {} \;
./path/to/file1.sh
./another/path/to/file2.txt
./blah/foo.php

Wenn ich nur mit diesen drei .sh-Dateien finden wollte, hätte ich Folgendes verwenden können:

find . -path '*/.svn' -prune -o -type f -name "*.sh" -exec gawk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print FILENAME }' {} \;
./path/to/file1.sh

1

Hier ist eine Bash-Funktion, die die Antwort von @ kurumi erweitert.

all_word_search() {
    gawk '
        BEGIN {
            for (i=ARGC-2; i>=1; i--) {
                search_terms[ARGV[i]] = 0;
                ARGV[i] = ARGV[i+1];
                delete ARGV[i+1];
            }
        }
        {
            for (i=1;i<=NF; i++) 
                if ($i in search_terms) 
                    search_terms[$1] = 1
        }
        END {
            for (word in search_terms) 
                if (search_terms[word] == 0) 
                    exit 1
        }
    ' "$@"
    return $?
}

Verwendung:

if all_word_search Dansk Norsk Svenska filename; then
    echo "all words found"
else
    echo "not all words found"
fi

1

Ich habe das mit zwei Schritten gemacht. Erstellen Sie eine Liste von CSV-Dateien in einer Datei. Mithilfe dieser Seitenkommentare habe ich zwei skriptlose Schritte ausgeführt, um das zu erhalten, was ich brauchte. Geben Sie einfach in das Terminal ein:

$ find /csv/file/dir -name '*.csv' > csv_list.txt
$ grep -q Svenska `cat csv_list.txt` && grep -q Norsk `cat csv_list.txt` && grep -l Dansk `cat csv_list.txt`

Es hat genau das getan, was ich brauchte - Dateinamen drucken, die alle drei Wörter enthalten.

Beachten Sie auch die Symbole wie `' "


1

Wenn Sie nur zwei Suchbegriffe benötigen, ist es wahrscheinlich am besten, jede Suche auszuführen und die Ergebnisse zu überschneiden:

 comm -12 <(grep -rl word1 . | sort) <(grep -rl word2 . | sort)

1

Wenn Sie Git installiert haben

git grep -l --all-match --no-index -e Dansk -e Norsk -e Svenska

Der --no-Index durchsucht Dateien im aktuellen Verzeichnis, die nicht von Git verwaltet werden. Dieser Befehl funktioniert also in jedem Verzeichnis, unabhängig davon, ob es sich um ein Git-Repository handelt oder nicht.


0

Ich hatte dieses Problem heute und alle Einzeiler hier sind mir nicht gelungen, weil die Dateien Leerzeichen in den Namen enthielten.

Das habe ich mir ausgedacht:

grep -ril <WORD1> | sed 's/.*/"&"/' | xargs grep -il <WORD2>
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.