Dlaczego „grep -q” zajmuje cały plik wejściowy?

23

Rozważ następujący plik wejściowy:

1
2
3
4

Bieganie

{ grep -q 2; cat; } < infile

nic nie drukuje. Spodziewałbym się, że to wydrukuje

3
4

Mogę uzyskać oczekiwany wynik, jeśli zmienię go na

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

Dlaczego pierwsze polecenie nie wypisuje oczekiwanego wyniku?
Jest to widoczny plik wejściowy i zgodnie ze standardem w OPCJACH :

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

i dalej, w części UŻYWANIE APLIKACJI (podkreśl moje):

Ta -qopcja umożliwia łatwe określenie, czy wzorzec (lub łańcuch) istnieje w grupie plików. Podczas przeszukiwania kilku plików zapewnia poprawę wydajności ( ponieważ może wyjść, gdy tylko znajdzie pierwsze dopasowanie ) [...]

Teraz, zgodnie z tym samym standardem (we wstępie , w obszarze PLIKI WEJŚCIOWE )

Gdy standardowe narzędzie odczytuje widoczny plik wejściowy i kończy się bezbłędnie, zanim osiągnie koniec pliku, narzędzie musi upewnić się, że przesunięcie pliku w otwartym opisie pliku jest właściwie ustawione tuż za ostatnim bajtem przetworzonym przez narzędzie [. ..]

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

Drugie polecenie jest równoważne pierwszemu tylko wtedy, gdy plik jest widoczny.


Dlaczego grep -qzużywa cały plik?


Ma gnu grepto znaczenie (choć Kusalananda właśnie potwierdził, że to samo dzieje się na OpenBSD)

don_crissti
źródło
OpenBSD grepto rozwidlenie czegoś zwanego FreeGrep , jeśli ktoś się zastanawia.
Kusalananda

Odpowiedzi:

37

grep zatrzymuje się wcześnie, ale buforuje dane wejściowe, więc test jest za krótki (i tak, zdaję sobie sprawę, że mój test jest niedoskonały, ponieważ nie można go zobaczyć):

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

zaczyna się o 6776 w moim systemie. Odpowiada to buforowi 32KiB używanemu domyślnie w GNU grep:

seq 1 6775 | wc

wyjścia

   6775    6775   32768

Zauważ, że POSIX wspomina tylko o poprawie wydajności

Podczas wyszukiwania kilku plików

To nie stawia żadnych oczekiwań na poprawę wydajności z powodu częściowego odczytu jednego pliku.

Stephen Kitt
źródło
2

Wynika to oczywiście z buforowania, które grepprzyspiesza. Istnieją narzędzia zaprojektowane specjalnie do odczytu dowolnej liczby znaków i nie więcej. Jednym z nich jest expect:

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

Nie mam systemu do wypróbowania tego, ale wierzę, expectże zje wszystko, dopóki nie napotka oczekiwanego ciągu ( 2), a następnie zakończy działanie, pozostawiając resztę danych wejściowych cat.

Dmitrij Grigoriew
źródło
1

Zmieszałeś sed i grep.

W przypadku komendy sed -2qmówi się, aby wyjść z bieżącej iteracji, jeśli w drugim wierszu -nopcja mówi cicho, więc wszystkie linie zostaną wyświetlone po drugim.

Polecenie grep jest domyślnie uruchamiane, aby wypisać wszystkie pasujące linie - ale -qopcja mówi, aby nie wypisywać niczego na standardowe wyjście. więc jeśli wejście zawiera „2”, będzie miało wartość wyjściową SUKCES, w przeciwnym razie BŁĄD. To, co to jest, zależy od systemu operacyjnego i powłoki. Tak więc zazwyczaj powinieneś sprawdzić, czy linia pasuje, sprawdzając wartość wyjściową procesu grep. Jest to przydatne w potoku, w którym chcesz wiedzieć, czy dane wejściowe zawierają pewną wartość jako test. Na przykład

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

W tym przypadku tak naprawdę nie zależy nam na wyświetleniu wszystkich pasujących linii, zależy nam tylko na tym, czy przynajmniej jedna z nich istnieje. report_crash_via_emailProces / funkcja może następnie zgaśnie i ponownie otworzyć plik, czy nie.

Jeśli chcesz, aby proces grep zatrzymał się po znalezieniu znaku „2” - domyślnie nie będzie sprawdzał każdej linii, aby sprawdzić, czy pasuje - musisz to powiedzieć. Do tego służy przełącznik wiersza poleceń -m <value>. Tak dla przypadku grep -q -m1 2.

użytkownik212377
źródło
6
Twoja odpowiedź jest użyteczną informacją do ogólnego użytku, grepale to pytanie dotyczy czegoś bardziej subtelnego i ezoterycznego. Wygląda na to, że przeczytałeś pytanie zbyt szybko, aby zrozumieć rzeczywiste zachowanie, którego dotyczy zapytanie. Również GNU grep robi poszukiwania zatrzymania w przypadku korzystania z -q(co jest dozwolone w cytat z specyfikacją POSIX): strona man dla państw GNU grep, że „Exit [S] natychmiast stanu zerowego jeżeli zostanie znaleziony” . FWIW, edytowałem twoje pytanie, aby pokazać, jak sformatować przyszłe posty. Witamy w Stack Exchange .
Anthony G - sprawiedliwość dla Moniki
To powiedziawszy, odpowiedź @ user212377 jest poprawna: w tym przypadku greppyta się, czy w pliku istnieje „2”, nic więcej i nic więcej. Do tego momentu nie zachowuje się jak sedi nie zużywa zapisów, a resztę pozostawia do dalszego przetwarzania. Czyta, dopóki nie wie, że jest „2” lub że nie ma, zamyka plik i zwraca wynik.
Keith Davies,
grepw rzeczywistości „zużywa cały plik” (ignorując kwestie związane z buforowaniem), jeśli szukany ciąg nie występuje w pliku (co można udowodnić tylko poprzez sprawdzenie całego pliku). Niezależnie od tego, odczyt pliku zatrzymuje się , plik jest zamykany, a SUKCES wraca.
Keith Davies,