Begrenzen Sie die Grep-Ausgabe auf kurze Zeilen


8

Ich benutze oft grep, um Dateien mit einem bestimmten Eintrag wie dem folgenden zu finden:

grep -R 'MyClassName'

Das Gute ist, dass es die Dateien und ihren Inhalt zurückgibt und die gefundene Zeichenfolge rot markiert. Das Schlimme ist, dass ich auch riesige Dateien habe, in denen der gesamte Text in einer großen Zeile geschrieben ist. Jetzt gibt grep zu viel aus, wenn Text in diesen großen Dateien gefunden wird. Gibt es eine Möglichkeit, die Ausgabe auf beispielsweise 5 Wörter links und rechts zu beschränken? Oder beschränken Sie die Ausgabe auf 30 Buchstaben links und rechts?


3
Pipe Ihre Ergebnisse durchcut
Rinzwind

Nehmen wir also an, das gesuchte Muster befindet sich an Position 50, aber Sie sagten, Sie möchten nur 30 Buchstaben. Was möchten Sie dann tun? Ignorieren Sie diese Zeile oder fügen Sie sie auch in die Ausgabe ein, aber schneiden Sie sie ab? Was genau möchten Sie einschränken - die Suche oder die Zeilen selbst?
Sergiy Kolodyazhnyy

1
@Rinzwind Ich verstehe nicht ganz, womit Sie etwas erreichen wollen cut, da es nur nach Trennzeichen oder nach Anzahl der Zeichen aufgeteilt wird. Wenn ich eine Linie MyClassNamedamit finde , kann sie sich jedoch irgendwo in der Linie befinden und nicht immer an derselben Position. Darüber hinaus kann es vorne und hinten zu einer Variation von Zeichen kommen, wodurch die Möglichkeit einer Aufteilung nach Trennzeichen aufgehoben wird.
Sokrates

1
@SergiyKolodyazhnyy Wenn eine positive Linie mit MyClassNamegefunden wurde, möchte ich als Ergebnis den Dateinamen und die x Zeichen links und rechts erhalten. x ist eine beliebige Zahl, die ich angegeben habe, zum Beispiel 30. Der Rest des Dateiinhalts wird ignoriert. Dies dient dazu, einen Kontext zu den übereinstimmenden Dateien zu erhalten und die Überlastung zu begrenzen.
Sokrates

1
@Rinzwind Welche Art von benutzerdefiniertem Trennzeichen würden Sie vorschlagen, cutwenn drei Dateien mit der folgenden Eingabe vorhanden sind: oiadfaosuoianavMyClassNameionaernaldfajdund /(/&%%§%/(§(/MyClassName&((/$/$/(§/$&und public class MyClassName { public static void main(String[] args) { } }?
Sokrates

Antworten:


15

grepselbst hat nur Optionen für den Kontext basierend auf Linien. Eine Alternative wird in diesem SU-Beitrag vorgeschlagen :

Eine Problemumgehung besteht darin, die Option "Nur Übereinstimmung" zu aktivieren und dann die Leistung von RegExp zu nutzen, um etwas mehr als Ihren Text zu erfassen:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}" ./filepath

Wenn Sie die Farbhervorhebung verwenden, können Sie natürlich immer wieder greifen, um nur die tatsächliche Übereinstimmung einzufärben:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}"  ./filepath | grep "WHAT_I_M_SEARCHING"

Als weitere Alternative würde ich vorschlagen, foldden Text zu verwenden und ihn dann zu erfassen, zum Beispiel:

fold -sw 80 input.txt | grep ...

Mit dieser -sOption werden foldPush-Wörter in die nächste Zeile verschoben, anstatt dazwischen zu brechen.

Oder verwenden Sie eine andere Methode, um die Eingabe basierend auf der Struktur Ihrer Eingabe in Zeilen aufzuteilen. (Der SU-Beitrag befasste sich zum Beispiel mit JSON, also wäre es besser , jqusw. zum hübschen Drucken zu verwenden und grep... oder einfach jqnur das Filtern selbst durchzuführen ... als eine der beiden oben angegebenen Alternativen.)


Diese GNU awk-Methode ist möglicherweise schneller:

gawk -v n=50 -v RS='MyClassName' '
  FNR > 1 { printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)}
  {p = substr($0, length - n); prt = RT}
' input.txt
  • Weisen Sie awk an, Datensätze nach dem Muster, an dem wir interessiert sind ( -v RS=...), und der Anzahl der Zeichen im Kontext ( -v n=...) zu teilen.
  • Jeder Datensatz nach dem ersten Datensatz ( FNR > 1) ist einer, bei dem awk eine Übereinstimmung mit dem Muster gefunden hat.
  • Wir drucken also nnachfolgende Zeichen aus der vorherigen Zeile ( p) und nführende Zeichen aus der aktuellen Zeile ( substr($0, 0, n)) zusammen mit dem übereinstimmenden Text für die vorherige Zeile (dh prt).
    • Wir setzen pund prt nach dem Drucken, so dass der Wert, den wir setzen, von der nächsten Zeile verwendet wird
    • RT ist ein GNUismus, deshalb ist dies GNU awk-spezifisch.

Für die rekursive Suche vielleicht:

find . -type f -exec gawk -v n=50 -v RS='MyClassName' 'FNR>1{printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)} {p = substr($0, length-n); prt = RT}' {} +

2
Ok, es funktioniert. Scheint, dass Regex ein gültiger Ansatz ist, also danke dafür. Die Bearbeitungszeit ist allerdings ziemlich groß. Ohne Regex wie in meinem obigen Beitrag dauert es 4,912 Sekunden und mit Regex wie in Ihrem Beitrag dauert es 3m39,312 Sekunden.
Sokrates

1
@Socrates sehen, ob die awk-Methode, die ich oben hinzugefügt habe, besser
funktioniert

1
Die foldMethode kann nur verwendet werden, wenn Sie sicher sind, dass die gesuchte Zeichenfolge nicht am Rand angezeigt wird, da sie sonst von ausgeblendet wird grep.
Melebius

1
@muru Danke für deinen Vorschlag mit gawk. Leider gibt der vorgeschlagene Befehl mit findzufälligen Dingen und ohne Dateinamen aus, wenn er auf meinem System ausgeführt wird. Außerdem bin ich nicht fließend genug awk, um den Befehl richtig zu analysieren. Derzeit greplöst Regex in Kombination mit die Angelegenheit möglicherweise nicht schnell, aber zuverlässig. Nochmals vielen Dank.
Sokrates

1
@Socrates Ich glaube, ich habe es geschafft, den Befehl awk zu reparieren. Mein mentales Modell war falsch, welche Zeilen, RTPräfixe usw. verwendet werden sollten.
Muru

1

Die Verwendung von Nur-Matching in Kombination mit einigen anderen Optionen (siehe unten) kann sehr nahe an dem liegen, was Sie suchen, ohne den in der anderen Antwort erwähnten Verarbeitungsaufwand für Regex

grep -RnHo 'MyClassName'
  • Bei numerischer Ausgabe wird die Zeilennummer der Übereinstimmung angezeigt
  • H Dateiname, zeigen Sie den Dateinamen am Anfang der Zeile des Spiels
  • o Nur Übereinstimmungen, nur die berechnete Zeichenfolge, nicht die gesamte Zeile

Zwar wird das Ergebnis viel schneller gefunden, es fehlen jedoch Informationen. Der Dateipfad wird angezeigt, die Zeilennummer wird angezeigt, aber die Textausgabe ist nur meine erste Suche MyClassName. Daher fehlt der Kontext.
Sokrates

grep -RnHo "MyClassName"und grep -Rno "MyClassName"haben die gleiche Ausgabe.
Sokrates

@Socrates Ausgabe ist nicht das gleiche ohne H im gleichen Verzeichnis
Robert Riedl

Das -oFlag könnte interessant sein, wenn der reguläre Ausdruck einen variablen Teil hat. Für eine feste Zeichenfolge ist es sinnlos, sie jedes Mal zu drucken. OP ist höchstwahrscheinlich am nahen Kontext interessiert.
Melebius

1
@Socrates, stimmt - Kontext fehlt, aber ich dachte, das war der Punkt? Ausgabe begrenzen? Sie können den Kontext erneut hinzufügen, indem Sie die Zeilen vor ( -B 1) oder nach ( -A 1) hinzufügen . Entschuldigung, dass ich nicht weiterhelfen konnte.
Robert Riedl
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.