Чому 'grep -q' споживає весь вхідний файл?


23

Розглянемо наступний вхідний файл:

1
2
3
4

Біг

{ grep -q 2; cat; } < infile

нічого не друкує. Я очікую, що він надрукує

3
4

Я можу отримати очікуваний вихід, якщо його зміню

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

Чому перша команда не друкує очікуваний вихід?
Це вхідний файл, який можна шукати і згідно стандарту в розділі ВАРІАНТИ :

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

і далі внизу, ВИКОРИСТАННЯ ЗАЯВКИ (підкресліть моє):

Цей -qпараметр забезпечує засіб легко визначити, чи існує шаблон (або рядок) у групі файлів. При пошуку декількох файлів він забезпечує поліпшення продуктивності ( оскільки він може вийти з роботи, як тільки знайде першу відповідність ) [...]

Тепер, за тим самим стандартом (у вступі , під ВХІДНІМ ФАЙЛАМ )

Коли стандартна утиліта зчитує шуканий вхідний файл і закінчується без помилки до того, як він дійде до кінця файлу, утиліта повинна гарантувати, що зміщення файлу у відкритому описі файлу належним чином розміщене біля останнього байта, обробленого утилітою [. ..]

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

Друга команда еквівалентна першій лише тоді, коли файл можна шукати.


Чому grep -qспоживається весь файл?


Це gnu grepякщо це має значення (хоча Кусалаланда просто підтвердив те, що відбувається на OpenBSD)


OpenBSD's grep- це вилка чогось, що називається FreeGrep , якщо хтось цікавиться.
Kusalananda

Відповіді:


37

grep зупиняється рано, але він зберігає свої дані, щоб ваш тест був занадто коротким (і так, я розумію, що мій тест є недосконалим, оскільки його не можна знайти):

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

починається з 6776 в моїй системі. Це відповідає буфері 32KiB, використовуваному за замовчуванням у GNU grep:

seq 1 6775 | wc

виходи

   6775    6775   32768

Зауважте, що POSIX згадує лише про підвищення продуктивності

При пошуку декількох файлів

Це не встановлює жодних очікувань щодо підвищення продуктивності завдяки частковому прочитанню одного файлу.


2

Очевидно, це пов’язано з буферизацією, grepяка сприяє прискоренню роботи. Є інструменти, спеціально розроблені для того, щоб прочитати стільки символів, скільки потрібно, і не більше. Один з них expect:

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

У мене немає системи, щоб це спробувати, але я вважаю expect, що з'їсть усе, поки не зустрінеться з очікуваною строкою ( 2), а потім припинить, залишивши решту вхідних даних для cat.


1

Ви плутаєте sed і grep.

Для команди sed -2qозначає, що вийти з поточної ітерації, якщо у другому рядку -nпараметр говорить, щоб тихо функціонував, тож ви отримаєте всі рядки після 2-го.

Команда grep працює за замовчуванням для виведення всіх відповідних рядків - але -qпараметр скаже, щоб нічого не виводити до stdout. Отже, якщо вхід містить "2", він буде мати значення виходу SUCCESS, інакше FAILURE. Що це таке, залежить від вашої операційної системи та оболонки. Отже, ти зазвичай скажеш, чи збігається рядок, вивчивши значення виходу процесу grep. Це корисно в конвеєрі, де ви хочете знати, чи містить ваш внесок якесь значення в якості тесту. Напр

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

У цьому випадку нам насправді не бачити всі відповідні рядки, нас просто цікавить, чи існує хоча б одна. Після цього report_crash_via_emailпроцес / функція може вимкнутись і знову відкрити файл, чи ні.

Якщо ви хочете, щоб ваш процес grep зупинився після того, як він знайде символ "2" - він за замовчуванням не буде перевіряти кожен рядок, шукаючи, чи відповідає він - вам потрібно сказати це зробити. Перемикач командного рядка для цього є -m <value>. Так що для вашого випадку, grep -q -m1 2.


6
Ваша відповідь є корисною інформацією для загального використання, grepале це питання задає щось більш тонке та езотеричне. Схоже, ви занадто швидко прочитали питання, щоб зрозуміти фактичну поведінку, яку запитують. Крім того , GNU grep робить зупинки пошуку при використанні -q(як дозволено в цитаті з специфікації POSIX): сторінка людей для GNU Grep говорить , що це «вихід негайно [S] з нульовим статусом якщо збіг знайдено» . FWIW, я відредагував ваше запитання, щоб показати, як ви можете форматувати майбутні повідомлення. Welcom для обміну стеками .
Ентоні Г - справедливість для Моніки

З цього приводу, відповідь @ user212377 правильна: у цьому випадку grepзапитується, чи існує "2" у файлі, нічого більше і нічого менше. Він не поводиться так, як sedі споживає записи до цього моменту і залишає залишок для подальшої обробки. Він читає, поки не дізнається, що є "2" або що немає, закриває файл і повертає результат.
Кіт Девіс

grepнасправді лише "споживає весь файл" (ігноруючи буферизацію), якщо пошуковий рядок відсутній у файлі (що можна довести лише при вивченні всього файлу). Більше того, читання файлів зупиняється , файл закривається, і SUCCESS повертається.
Кіт Девіс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.