Warum verbraucht 'grep -q' die gesamte Eingabedatei?

23

Betrachten Sie die folgende Eingabedatei:

1
2
3
4

Laufen

{ grep -q 2; cat; } < infile

druckt nichts. Ich würde erwarten, dass es gedruckt wird

3
4

Ich kann die erwartete Ausgabe erhalten, wenn ich sie in ändere

{ sed -n 2q; cat; } < infile

Warum gibt der erste Befehl die erwartete Ausgabe nicht aus?
Es ist eine suchbare Eingabedatei und entspricht dem Standard unter OPTIONEN :

-q
      Quiet. Nothing shall be written to the standard output, regardless of 
      matching lines. Exit with zero status if an input line is selected.

und weiter unten unter ANWENDUNGSVERWENDUNG (meine hervorheben):

Mit dieser -qOption können Sie auf einfache Weise feststellen, ob ein Muster (oder eine Zeichenfolge) in einer Gruppe von Dateien vorhanden ist. Beim Durchsuchen mehrerer Dateien wird eine Leistungsverbesserung erzielt ( da die Suche beendet werden kann, sobald die erste Übereinstimmung gefunden wird ). [...]

Jetzt nach dem gleichen Standard (in der Einleitung unter INPUT FILES )

Wenn ein Standarddienstprogramm eine suchbare Eingabedatei liest und ohne Fehler beendet, bevor das Dateiende erreicht ist, muss das Dienstprogramm sicherstellen, dass der Dateiversatz in der Beschreibung der geöffneten Datei genau hinter dem letzten vom Dienstprogramm verarbeiteten Byte [ positioniert ist . ..]

tail -n +2 file
(sed -n 1q; cat) < file
...

Der zweite Befehl entspricht nur dann dem ersten, wenn die Datei gesucht werden kann.


Warum grep -qverbraucht die ganze Datei?


Dies ist, gnu grepwenn es darauf ankommt (obwohl Kusalananda gerade bestätigt hat, dass dasselbe unter OpenBSD passiert)

don_crissti
quelle
OpenBSD grepist eine Abzweigung von etwas, das sich FreeGrep nennt , wenn sich jemand wundert.
Kusalananda

Antworten:

37

grep hört zwar früh auf, puffert aber die Eingabe, sodass Ihr Test zu kurz ist (und ja, ich stelle fest, dass mein Test unvollständig ist, da er nicht suchbar ist):

seq 1 10000 | (grep -q 2; cat)

Startet bei 6776 auf meinem System. Das stimmt mit dem 32-KB-Puffer überein, der standardmäßig in GNU grep verwendet wird:

seq 1 6775 | wc

Ausgänge

   6775    6775   32768

Beachten Sie, dass POSIX nur Leistungsverbesserungen erwähnt

Bei der Suche nach mehreren Dateien

Das lässt keine Erwartungen an Leistungsverbesserungen aufgrund des teilweisen Lesens einer einzelnen Datei aufkommen.

Stephen Kitt
quelle
2

Dies liegt offensichtlich an der Pufferung, grepdie die Dinge beschleunigt. Es gibt Tools, die speziell dafür ausgelegt sind, so viele Zeichen wie gewünscht zu lesen und nicht mehr. Einer von ihnen ist expect:

{ expect -c "log_user 0; expect 2"; cat; } < infile

Ich habe kein System, um das anzuprobieren, aber ich glaube, expectes frisst alles auf, bis es auf die erwartete Zeichenfolge ( 2) stößt , und endet dann und lässt den Rest der Eingabe für cat.

Dmitry Grigoryev
quelle
1

Sie sind sed und grep verwirrend.

Für den Befehl sed -2qheißt es, die aktuelle Iteration zu beenden, wenn in der zweiten Zeile die -nOption "Leise arbeiten" angegeben ist, sodass nach der zweiten Zeile alle Zeilen angezeigt werden.

Der Befehl grep wird standardmäßig ausgeführt, um alle übereinstimmenden Zeilen auszugeben. Die -qOption say gibt jedoch nichts an stdout aus. Wenn die Eingabe also eine "2" enthält, hat sie den Ausgangswert SUCCESS, andernfalls FAILURE. Was das ist, hängt von Ihrem Betriebssystem und der Shell ab. In der Regel können Sie also feststellen, ob eine Zeile übereinstimmt, indem Sie den Exit-Wert des grep-Prozesses untersuchen. Dies ist nützlich in einer Pipeline, in der Sie wissen möchten, ob Ihre Eingabe als Test einen Wert enthält. Z.B

if grep -q 'crash' <somelog.log ; then report_crash_via_email ; fi

In diesem Fall ist es uns wirklich egal, ob alle übereinstimmenden Zeilen angezeigt werden. Es ist uns nur wichtig, ob mindestens eine vorhanden ist. Der report_crash_via_emailProzess / die Funktion kann dann losgehen und die Datei erneut öffnen oder nicht.

Wenn Sie möchten, dass Ihr grep-Prozess nach dem Finden des Zeichens "2" STOPPT - es wird nicht standardmäßig jede Zeile daraufhin überprüft, ob sie übereinstimmt - müssen Sie ihm dies mitteilen. Der Befehlszeilenschalter dafür ist -m <value>. Also für deinen Fall grep -q -m1 2.

user212377
quelle
6
Ihre Antwort ist eine nützliche Information für den allgemeinen Gebrauch, grepaber bei dieser Frage geht es um etwas Feinsinnigeres und Esoterischeres. Anscheinend haben Sie die Frage zu schnell gelesen, um das tatsächlich abgefragte Verhalten zu verstehen. Außerdem stoppt GNU grep die Suche, wenn es mit -q(wie im Zitat aus der POSIX-Spezifikation erlaubt) verwendet wird: Die Manpage für GNU grep gibt an, dass es "[s] sofort mit dem Status Null beendet, wenn eine Übereinstimmung gefunden wird" . FWIW, ich habe Ihre Frage bearbeitet, um zu zeigen, wie Sie zukünftige Beiträge formatieren können. Willkommen bei Stack Exchange .
Anthony G - Gerechtigkeit für Monica
Das heißt, die Antwort von @ user212377 ist richtig: In diesem Fall grepwird gefragt, ob in der Datei '2' vorhanden ist, nicht mehr und nicht weniger. Bis zu diesem Punkt verhält es sich nicht wie sedAufzeichnungen und verbraucht sie und überlässt den Rest der Aufzeichnungen der weiteren Verarbeitung. Es liest, bis es weiß, dass es eine '2' gibt oder nicht, schließt die Datei und gibt das Ergebnis zurück.
Keith Davies
grepTatsächlich "verbraucht nur die gesamte Datei" (ohne Berücksichtigung von Überlegungen zum Puffern), wenn die Suchzeichenfolge in der Datei nicht vorhanden ist (was nur durch Untersuchen der gesamten Datei bewiesen werden kann). Wenn dies nicht der Fall ist, stoppt das Lesen der Datei , die Datei wird geschlossen und SUCCESS wird zurückgegeben.
Keith Davies