Команды Cygwin 2.9.0 cat/tac не выполняются для больших файлов при передаче по каналу grep -q -m1

Я вижу странное поведение при использовании Cygwin x64 2.9.0 в Windows 10 Pro x64. Команда, которую я пытаюсь выполнить, следующая:

tac <file> | grep -q -m1 -F "literal string"

Приведенная выше команда успешно выполняется на всех маленьких файлах, которые я к ней добавляю (маленькие означает <= 15 КБ). Это также успешно, если окончательное появление literal string находится в начале файла (например, literal string появляется в верхней части файла и больше нигде). Наконец, это также успешно, когда ни один из {-q, -m1} флаги передаются grep команда.

Тем не менее, когда файл составляет около 680 КБ, и literal string появляется в конце файла, затем tac команда выводит "tac: write error" в STDERR. Несмотря на эту ошибку, команда, кажется, успешно выполнила печать соответствующей строки для вывода (когда -q флаг опускается) и получает соответствующее возвращаемое значение из grep,

Дальнейшее тестирование показало, что эта же ошибка возникает при использовании catкроме literal string должен появиться в начале файла, чтобы сгенерировать ошибку, а сгенерированная ошибка - "cat: write error: на устройстве не осталось места".

Обратите внимание, что это происходит только если хотя бы один из {-m1, -q} параметры передаются в grep команда, совпадение находится рядом с первой обработанной строкой файла (для cat это в начале, для tac это ближе к концу), и файл большой.

Я управлял df и сообщает о 14 МБ, доступных на диске Cygwin, при этом 60 ГБ свободно на самом диске. Я знаю, что могу просто перенаправить STDERR на устройство NUL, но это выглядит как хакерский обходной путь. Кто-нибудь знает, как это исправить правильно?

НАЧАТЬ РЕДАКТИРОВАТЬ

Я нашел еще одно сообщение об этой же ошибке от мая 2017 года, но решение не было представлено. ОП другого поста действительно указывает, что он думает, что это ограничение размера буфера канала (возможно, в Windows, возможно в Cygwin).

1 ответ

Решение

Я обнаружил несколько обходных путей. Просто измените команду:

tac <file> | grep -q -m1 -F "literal string"

к одному из:

bash -c "tac <file> | grep -q -m1 -F 'literal string'"
stdbuf -o L tac <file> | grep -q -m1 -F "literal string"

Я думаю, что первый работает, потому что он использует канал Linux, а второй, потому что он заставляет tac вывод команды должен быть буферизован. Обе эти формы устраняют ошибку.

Так как это работает, я думаю, проблема в том, что grep прекращает обработку входного буфера, как только он находит первое совпадение, но tac продолжает обрабатывать ввод. Как только буфер заполнен (вероятно, 64 кБ), буфер блокирует и tac выходит с указанной ошибкой. Тем не менее, так как tac перед обработкой успешно обработал строку, которая мне нужна, все работает, как задумано.

Сроки этих опций указывает, что вызов bash это более быстрый вариант. Это, вероятно, потому, что с помощью канала Linux, tac может вернуться сразу один раз grep находит первый матч.

Другие вопросы по тегам