Почему '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Вариант обеспечивает средство легко определить , существует ли или нет образец (или строка) в группе файлов. При поиске в нескольких файлах это обеспечивает улучшение производительности ( потому что он может выйти, как только найдет первое совпадение ) [...]

Теперь, в соответствии с тем же стандартом (во введении , под INPUT FILES )

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

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

Вторая команда эквивалентна первой, только если файл доступен для поиска.


Почему grep -qпотребляет весь файл?


Это gnu grepесли это имеет значение (хотя Кусалананда только что подтвердил, что то же самое происходит в OpenBSD)


OpenBSD grep- это форк чего-то, называемого FreeGrep , если кому-то интересно.
Кусалананда

Ответы:


37

grep останавливается рано, но буферизует входные данные, поэтому ваш тест слишком короткий (и да, я понимаю, что мой тест несовершенен, так как его нельзя найти):

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

начинается в 6776 в моей системе. Это соответствует буферу 32 КБ, используемому по умолчанию в 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опция говорит, что ничего не выводится в стандартный вывод. таким образом, если вход содержит «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, я отредактировал ваш вопрос, чтобы показать, как вы можете форматировать будущие сообщения. Добро пожаловать в стек обмена .
Энтони Дж - справедливость для Моники

Тем не менее, ответ @ user212377 правильный: в этом случае grepспрашивается, существует ли в файле «2», не больше и не меньше. Он не ведет себя как sedи не использует записи до этого момента и оставляет остаток для дальнейшей обработки. Он читает до тех пор, пока не узнает, что есть «2» или что нет, закрывает файл и возвращает результат.
Кит Дэвис

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