Как эффективно искать список строк в большом каталоге кода

status="curl \"${server}:${port}\""  >/tmp/tmp.txt
host_check='grep "<html" /tmp/tmp.txt'
5
04.03.2016, 17:17
3 ответа

По крайней мере, его можно упростить до:

set -f # needed if you're using the split+glob operator and don't want the
       # glob part

for key in $(cat /tmp/listOfKeys.txt); do
   grep -riFqe "$key" . ||
    printf '%s\n' "$key has no occurrence"
done

Что прекратит поиск после первого появления ключа и не будет рассматривать ключ как регулярное выражение (или возможный вариант grep).

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

find . -type f -size +0 -printf '%p\0' | awk '
  ARGIND == 2 {ARGV[ARGC++] = $0; next}
  ARGIND == 4 {a[tolower($0)]; n++; next}
  {
    l = tolower($0)
    for (i in a) if (index(l, i)) {
      delete a[i]
      if (!--n) exit
    }
  }
  END {
    for (i in a) print i, "has no occurrence"
  }' RS='\0' - RS='\n' /tmp/listOfKeys.txt

Он оптимизирован в том смысле, что перестанет искать ключ, как только увидит его, и остановится, как только все ключи будут найдены, и прочитает файлы только один раз.

Предполагается, что ключи уникальны в listOfKeys.txt. Он выводит ключи в нижнем регистре.

Приведенные выше GNUизмы - это -printf '%p\0', ARGIND и способность awk обрабатывать записи с разделителями NUL. Первые две проблемы можно решить с помощью:

find . -type f -size +0 -exec printf '%s\0' {} + | awk '
  step == 1 {ARGV[ARGC++] = $0; next}
  step == 2 {a[tolower($0)]; n++; next}
  {
    l = tolower($0)
    for (i in a) if (index(l, i)) {
      delete a[i]
      if (!--n) exit
    }
  }
  END {
    for (i in a) print i, "has no occurrence"
  }' step=1 RS='\0' - step=2 RS='\n' /tmp/listOfKeys.txt step=3

Третью можно решить с помощью трюков вроде этого, но это, вероятно, не стоит усилий. Смотрите решение Barefoot IO, чтобы обойти проблему полностью.

5
27.01.2020, 20:34

GNU grep (как и большинство известных мне вариантов) предлагает опцию -f, которая делает именно то, что вам нужно. Вариант fgrep рассматривает входные строки как обычные строки, а не как regex'ы.

fgrep -rio -f /tmp/listOfKeys.txt .

А если вы просто хотите проверить, найдено ли хотя бы одно совпадение, добавьте опцию -q. По замечанию Стефана, если вам нужно знать, какие строки были не найдены, добавьте опцию -h и затем передайте через эту обычную идиому awk:

fgrep -h -rio -f /tmp/listOfKeys.txt . |
awk '{$0=tolower($0)}; !seen[$0]++' |
fgrep -v -i -x -f - /tmp/listOfKeys.txt

Второй fgrep теперь использует результат первого fgrep (уникальный случай нечувствителен), инвертирует смысл и показывает несовпадающие строки из ключевого файла.

5
27.01.2020, 20:34

Портативный, POSIX-совместимый перевод подхода gawk Стефана Шазеласа:

find . -type f -exec cat {} + |
awk '
    FNR==NR {keys[tolower($0)]; n++; next}
    {
        s = tolower($0)
        for (k in keys) 
            if (index(s, k)) {
                delete keys[k]
                if (!--n)
                    exit
            }
    }
    END {
        for (k in keys) print k, "has no occurrence"
    }
' /tmp/listOfKeys.txt -

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

1
27.01.2020, 20:34

Теги

Похожие вопросы