Grep вызывает зависание всей системы при сравнении двух файлов

393
confetti

Я хотел сравнить два файла и проверить, существует ли каждая строка file1в file2. Моя первая попытка: grep -v -f file2 file1. Это приводило к множеству синтаксических ошибок ( но ничего не зависало ). Я быстро обнаружил, что это потому, что мне нужно использовать, -Fкак описано здесь . Итак, я побежал, grep -Fvf file2 file2и через несколько секунд вся моя система зависла на несколько минут, пока xorg полностью не рухнул.

Я смог сфотографировать застывший экран: resource manager

И как только я наконец смог войти в tty2, меня встретили с этим: errors

Вопросы:

  1. Была ли причина зависания системы просто в том, что ей не хватило оперативной памяти, или есть еще?
  2. Почему grep использует ~ 14 ГБ ОЗУ (и хочет больше) для сравнения двух файлов размером 250 МБ?
  3. Я мог бы использовать инструменты для ограничения оперативной памяти, которые может использовать grep, но AFAIK - все они просто убьют процесс, когда он достигнет x ГБ ОЗУ, так что это мне не поможет. Что делать в такой ситуации? Давайте предположим, что мы должны использовать grep.

Изменить: я уже нашел обходной путь без grep. Мне действительно любопытно, почему и как это все еще может произойти. + 14 ГБ ОЗУ для двух 250 МБ файлов мне просто кажется странным. Я не ищу альтернативу тому, как я могу сравнить свои файлы с этим вопросом.

1
По какой причине вы не использовали команду diff? Это казалось бы более подходящим инструментом для этой работы. davidgo 5 лет назад 0
@davidgo Я попытался использовать diff следующим образом: `diff --new-line-format =" "--unchanged-line-format =" "F1 F2`, но это не сработало. Я тоже пытался сортировать строки. Он напечатал тысячи строк в обоих файлах. Сортировка, вероятно, усугубила ситуацию, поскольку файлы уже отсортированы (например, нижняя часть файла1 должна совпадать с нижней частью файла2), а сортировка (в алфавитном порядке) только усугубит ситуацию. Я получил то, что хотел, просто обрезав первые ~ 210 тыс. Строк файла2, а затем снова применил `diff`, чтобы убедиться, что содержимое точно идентично. Я все еще интересуюсь ответами о grep, использующем так много оперативной памяти. confetti 5 лет назад 0
Почему бы вам просто не использовать "diff file1 file2"? (--new-line-format и --unchanged-line-format даже не существуют в моей версии diff) davidgo 5 лет назад 0
@davidgo Потому что это показало бы мне все различия, когда я просто хочу знать, существует ли каждая строка в file1 в file2. Строки + 200k в file2, которых нет в file1, будут заполнять вывод. Я отредактировал мой вопрос, чтобы быть более ясным. Я действительно больше после объяснения поведения grep, чем получения альтернативы для операции. confetti 5 лет назад 0

1 ответ на вопрос

6
dirkt
  1. Причина была определенно исчерпана памятью.

  2. Поскольку вы не «сравниваете два файла», вы используете один файл размером 250 МБ в качестве источника шаблонов для grep. Grep компилирует эти шаблоны в вариант детерминированного конечного автомата, и представление этих DFA занимает память. Если у вас много шаблонов (например, 250 МБ шаблонов), это занимает много места, потому что преобразование недетерминированного конечного автомата, который соответствует многим шаблонам, в DFA может вызвать экспоненциальный взрыв.

grepсделан для очень эффективного поиска нескольких шаблонов в одном или нескольких больших файлах. Это не сделано, чтобы "сравнить" файлы. Если вы попытаетесь использовать его для этого, все может пойти не так. Как они сделали в вашем случае.

Сложность имеет значение, поэтому вы узнаете об О-нотации и обо всех этих причудливых вещах.

  1. В такой ситуации вы используете программу, созданную для вашей ситуации, а не программу, которая использует алгоритм, который экспоненциально для вашей задачи.

Вы сказали, что не хотите знать альтернативу, но поскольку в ней используется менее известный инструмент, я все равно скажу вам:

Если вопрос заключается в том, «существует ли каждая строка файла1 также в файле2, независимо от порядка», то вы должны отсортировать оба файла, а затем использовать comm, который ожидает отсортированные файлы, и выдает (1) строки в файле1, но не в file2, (2) строки в file2, но не в file1 и (3) строки в обоих файлах, для вашего удобства.

Именно то, что я искал в ответе, большое спасибо за отличное объяснение. confetti 5 лет назад 0