Wie suche ich nach Text in einer Datei, wobei Zeilenumbrüche ignoriert werden?

11

Ich möchte nach Text suchen, der in einer Datei über mehrere Zeilen verteilt sein kann. Ein Grep, der Zeilenumbrüche ignoriert und die übereinstimmende Zeilenbreite zurückgibt.

zB würde ich suchen is an example fileund erwarten, dass es in der folgenden Datei gefunden wird:

Dies ist
eine
Beispieldatei.

Um nicht von führenden oder nachfolgenden Leerzeichen abhängig zu sein, ist es möglicherweise am besten, alle Formen von Leerzeichen vollständig zu ignorieren (im Idealfall wird jede Folge von Leerzeichen als ein einzelnes Leerzeichen behandelt).


Eine nicht ideale Lösung besteht darin tr '\n' ' ' | grep, zwischen Übereinstimmungen und Nichtübereinstimmungen zu unterscheiden, die Übereinstimmung jedoch nicht anzuzeigen und auch nicht gut mit großen Dateien umzugehen.

Nikana Reklawyks
quelle
auf SO (keine endgültige Antwort): stackoverflow.com/q/1858312/1449460
Nikana Reklawyks
Als Randnotiz scheint die Suche von Emacs den Job zu machen ( isearch-forward)
Nikana Reklawyks
Vims auch : /This\_sis. Für weitere Details : :help \_s.
lcd047
Fügen Sie diese Zeile am Ende Ihrer Suchzeile hinzu: tr -n "\ n" Dadurch werden alle neuen Zeilen entfernt. Ich hoffe das hilft!
Dan Howel

Antworten:

12

Die GNU grepkann es tun

grep -z 'is\san\sexample\sfile.' file

Um einige Punkte zu erfüllen, die in Kommentaren auftreten, wurden einige Änderungen am Skript vorgenommen:

 grep -oz '^[^\n]*\bis\s*an\s*example\s*file\.[^\n]*' file

In Bezug auf große Dateien habe ich keine Vorstellung von Speicherbeschränkungen, aber im Falle eines Problems können Sie diese frei verwenden sed

sed '/\bis\b/{
          :1
          N
          /file\.\|\(\n.*\)\{3\}/!b1
         }
     /\<is\s*an\s*example\s*file\./p
     D' file

das hält nicht mehr als 4 Zeilen (weil 4 Wörter im Muster) im Speicher ( \(\n.*\)\{3\}).

Costas
quelle
4
Wie Sie sicher wissen, weist die -zOption grepan, Zeilenumbrüche als normale Textzeichen zu behandeln und nach Null-Bytes zu suchen, um Datensätze zu trennen. In einer Textdatei ohne Nullbytes (dh im typischen Fall) grep -zwird die gesamte Datei als eine Zeile behandelt. (1) Dies wirft die Frage auf, wie gut es mit großen Dateien umgehen kann, und (2) wenn es eine Übereinstimmung findet, schreibt es die gesamte Datei aus und gibt keinen Hinweis auf den Ort der Übereinstimmung. Außerdem (3) sagte das OP: „Idealerweise wird jede Folge von Leerzeichen als ein einzelnes Leerzeichen behandelt. Sie sollten sie also verwenden \s+und hinzufügen -E.
G-Man sagt "Reinstate Monica"
1
@ G-Man Danke für den Kommentar. Bitte siehe bearbeitete Antwort.
Costas
1
(0) Ah -o; Das vergesse ich immer wieder. Clevere Art, es zu benutzen. (1) Ihre neue grepAntwort beginnt ^[\n]*; das ist ein Tippfehler für [^\n]*. (2) Ich sagte \s+absichtlich.  be\s*littlewird übereinstimmen belittleund care\s*lesswird übereinstimmen careless. Aber ich denke, das ist ein kleines Problem. Und wenn Sie nicht verwenden möchten -E, können Sie "die Version des armen Mannes" von verwenden \s+, nämlich \s\s*. (3) Netter sedBefehl. Es kann fehlschlagen, wenn Leerzeilen vorhanden sind (die Phrase mit vier Wörtern kann sich also über mehr als vier Zeilen erstrecken). Ich konnte das durch Hinzufügen beheben s/\n\s*\n/\n/.
G-Man sagt "Reinstate Monica"
@ G-Man Danke nochmal. Ihre Kommentare sind sehr nützlich. Ich habe versucht, mehr oder weniger portablen Code zu veröffentlichen, weil mich berühmte Mitglieder jedes Mal dazu drängen. Auf jeden Fall auch ohne -ESie Stahl +in \s\+Form verwenden können. Leere Linien innerhalb des Musters scheinen erfunden zu sein.
Costas
Ich dachte an paginierte Textdokumente wie RFCs - ISTR, bei denen Manpages auf einigen Systemen so aussehen (oder es taten ) -, aber bei weiterem Nachdenken fällt mir ein, dass die meisten dieser Dokumente Seitenkopfzeilen und / oder Fußzeilen haben (s), die entfernt werden müssten, bevor Sie grepauf Phrasen hoffen können.
G-Man sagt "Reinstate Monica"
7

Versuche dies:

pcregrep -M '\bThis\s+is\b' <<EOT
This
is
an example
file.
EOT
lcd047
quelle
Muss ich \s5 Mal eingeben, wenn ich nach "Dies ist ein sehr langes Muster" suche?
Nikana Reklawyks
1
Ja, der Punkt \sstimmt mit Leerzeichen überein, und Newline ist ein "Leerzeichen".
lcd047
Ich meine, was ist, wenn die Datei ist This\nis a very\nlong patternund ich nicht weiß, wo die Zeilenumbrüche auftreten könnten. Ich müsste suchen This\sis\sa\svery\slong\spattern, oder? (was langweilig wird, wenn die Länge des Musters zunimmt oder von einer anderen Stelle eingefügt wird)
Nikana Reklawyks
2
Dann machst du es so : pcregrep -M "$( echo 'This is a very long pattern' | sed 's/ /\\s+/g' )" file.
lcd047