Найти файлы, содержащие несколько ключевых слов в любом месте файла

Я рекомендую, чтобы ваша запись localhost в вашем /etc/freeradius/clients.conf была:

client localhost {
    ipaddr = 127.0.0.1
    secret      = secret
    require_message_authenticator = no
    nas_type     = other
}

nas_type , обычно это cisco , livingston ] или другое .

ПРОВЕРЬТЕ ТАКЖЕ свой hostapd.conf: вы также должны установить ieee8021x = 1

Что касается require_message_authenticator , более полезно отправлять сообщения CoA / disconnect, и чтобы он был настроен вами требуется radius_das_require_message_authenticator = 1 в hostapd.conf . Я бы обошелся без этого [пока].

Чтобы запустить freeradius -X для отладки, вам также необходимо остановить демон freeradius , так как из вашей команды netstat мы видим, что 1812 слушает, и как таковая служба это работает; отладка предполагает, что вы заново загружаете его в однопоточном демоне.

Freeradius будет загружаться до тех пор, пока не появится сообщение «Прослушивание ... Готово обрабатывать запросы». Настоящее веселье начинается здесь, все после этого предназначено для отладки транзакций, аутентифицирующих клиентов Wi-Fi. В любой момент вы можете выйти с помощью ^ C, и, если вы хотите запустить службу, вы можете запустить ее снова.

Следует отметить, что пароль / secret в clients.conf должен совпадать с используемым для команд отладки или в hostpad.conf. В противном случае freeradius отклонит любые пакеты с другим секретом.

Что касается команд отладки, которые мы использовали для отладки запросов FreeRadius, в интересах других:

echo "Message-Authenticator = 0x00,FreeRADIUS-Statistics-Type = 31" | radclient localhost:18121 status adminsecret

Эта команда проверяет статус, но у вас должен быть активен статус vhost.

и:

echo "User-Name=test,User-Password=mypass,Framed-Protocol=PPP " | /usr/local/bin/radclient localhost:1812 auth secret

Что касается будущего, после того, как вы станете немного более удобными с Freeradius и если эта установка будет использоваться в производстве, я бы посоветовал получить и скомпилировать текущую версию 3.0.13 с github, так как там бесчисленные исправления ошибок.

17
13.04.2017, 15:36
5 ответов

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

#!/bin/bash

# Usage: multi [z]grep PATTERNS -- FILES

command=$1

# first two arguments constitute the first command
command_head="$1 -le '$2'"
shift 2

# arguments before double-dash are keywords to be piped with xargs
while (("$#")) && [ "$1" != -- ] ; do
  command_tail+="| xargs $command -le '$1' "
  shift
done
shift

# remaining arguments are files
eval "$command_head $@ $command_tail"

Так что теперь написание multi grep one two three -- * эквивалентно моему оригинальному предложению и выполняется за то же время. Я также могу легко использовать его для сжатых файлов, используя zgrep в качестве первого аргумента.

Другие решения

Я также экспериментировал со сценарием на Python, используя две стратегии: поиск всех ключевых слов построчно и поиск по ключевому слову во всем файле. В моем случае вторая стратегия была быстрее. Но она оказалась медленнее, чем просто использование grep, завершившись за 33 секунды. Построчный поиск по ключевому слову завершился за 60 секунд.

#!/usr/bin/python3

import gzip, sys

i = sys.argv.index('--')
patterns = sys.argv[1:i]
files = sys.argv[i+1:]

for f in files:
  with (gzip.open if f.endswith('.gz') else open)(f, 'rt') as s:
    txt = s.read()
    if all(p in txt for p in patterns):
      print(f)

Скрипт приведенный terdon завершил работу за 54 секунды. На самом деле это заняло 39 секунд времени стены, потому что мой процессор двухъядерный. Что интересно, потому что мой скрипт Python занял 49 секунд времени ожидания (а grep - 29 секунд).

Скрипт cas не смог завершиться за разумное время, даже на меньшем количестве файлов, которые обрабатывались с помощью grep менее 4 секунд, поэтому мне пришлось его убить.

Но его оригинальное предложение awk, хотя оно и медленнее, чем grep как есть, имеет потенциальное преимущество. В некоторых случаях, по крайней мере, по моему опыту, можно ожидать, что все ключевые слова должны появиться где-то в голове файла, если они вообще есть в файле. Это дает данному решению значительный прирост производительности:

for f in *; do
  zcat $f | awk -v F=$f \
    'NR>100 {exit} /one/ {a++} /two/ {b++} /three/ {c++} a&&b&&c {print F; exit}'
done

Завершение работы происходит за четверть секунды, по сравнению с 25 секундами.

Конечно, у нас может не быть преимущества поиска ключевых слов, которые, как известно, встречаются в начале файлов. В таком случае решение без NR>100 {exit} занимает 63 секунды (50 с времени стены).

Несжатые файлы

Нет существенной разницы во времени работы между моим решением grep и предложением awk от cas, оба решения выполняются за доли секунды.

Обратите внимание, что инициализация переменной FNR == 1 { f1=f2=f3=0; } в этом случае обязательна для сброса счетчиков для каждого последующего обрабатываемого файла. По существу, это решение требует редактирования команды в трех местах, если вы хотите изменить ключевое слово или добавить новые. С другой стороны, при использовании grep вы можете просто добавить | xargs grep -l four или отредактировать нужное ключевое слово.

Недостатком решения grep, использующего подстановку команд, является то, что оно зависнет, если в любом месте цепочки, до последнего шага, нет подходящих файлов. Это не влияет на вариант xargs, потому что труба будет прервана, как только grep вернет ненулевой статус. Я обновил свой сценарий, чтобы использовать xargs, так что мне не придется обрабатывать это самому, что упрощает сценарий.

2
20.08.2021, 12:04

Для сжатых файлов вы можете перебрать каждый файл и сначала распаковать. Затем, с немного измененной версией других ответов, вы можете сделать:

for f in *; do 
    zcat -f "$f" | perl -ln00e '/one/&&/two/&&/three/ && exit(0); }{ exit(1)' && 
        printf '%s\n' "$f"
done

Сценарий Perl завершится с 0 статусом (успех), если были найдены все три строки. } { - это сокращение Perl для END {} . Все, что следует за ним, будет выполнено после обработки всех входных данных. Таким образом, сценарий завершится с не нулевым статусом выхода, если не все строки были найдены. Следовательно, && printf '% s \ n' "$ f" напечатает имя файла, только если все три были найдены.

Или, чтобы не загружать файл в память:

for f in *; do 
    zcat -f "$f" 2>/dev/null | 
        perl -lne '$k++ if /one/; $l++ if /two/; $m++ if /three/;  
                   exit(0) if $k && $l && $m; }{ exit(1)' && 
    printf '%s\n' "$f"
done

Наконец, если вы действительно хотите сделать все это в сценарии, вы можете сделать:

#!/usr/bin/env perl

use strict;
use warnings;

## Get the target strings and file names. The first three
## arguments are assumed to be the strings, the rest are
## taken as target files.
my ($str1, $str2, $str3, @files) = @ARGV;

FILE:foreach my $file (@files) {
    my $fh;
    my ($k,$l,$m)=(0,0,0);
    ## only process regular files
    next unless -f $file ;
    ## Open the file in the right mode
    $file=~/.gz$/ ? open($fh,"-|", "zcat $file") : open($fh, $file);
    ## Read through each line
    while (<$fh>) {
        $k++ if /$str1/;
        $l++ if /$str2/;
        $m++ if /$str3/;
        ## If all 3 have been found
        if ($k && $l && $m){
            ## Print the file name
            print "$file\n";
            ## Move to the net file
            next FILE;
        }
    }
    close($fh);
}

Сохраните приведенный выше сценарий как foo.pl где-нибудь в вашем $ PATH , сделайте его исполняемым и запустите следующим образом:

foo.pl one two three *
3
20.08.2021, 12:04

Другой вариант - передавать слова по одному в xargs , чтобы он запускал grep для файла. xargs можно заставить выйти, как только вызов grep вернет ошибку, вернув ему 255 (см. Документацию xargs ). Конечно, порождение оболочек и разветвление, задействованное в этом решении, скорее всего, значительно замедлит его

printf '%s\n' one two three | xargs -n 1 sh -c 'grep -q $2 $1 || exit 255' _ file

и зациклит

for f in *; do
    if printf '%s\n' one two three | xargs -n 1 sh -c 'grep -q $2 $1 || exit 255' _ "$f"
    then
         printf '%s\n' "$f"
    fi
done
0
20.08.2021, 12:04

Установить разделитель записей на . , чтобы awk обрабатывал весь файл как одну строку:

awk -v RS='.' '/one/&&/two/&&/three/{print FILENAME}' *

Аналогично perl :

perl -ln00e '/one/&&/two/&&/three/ && print $ARGV' *
12
20.08.2021, 12:04
awk 'FNR == 1 { f1=f2=f3=0; };

     /one/   { f1++ };
     /two/   { f2++ };
     /three/ { f3++ };

     f1 && f2 && f3 {
       print FILENAME;
       nextfile;
     }' *

Если вы хотите автоматически обрабатывать файлы с gzip-сжатием, либо запустите это в цикле с zcat (медленно и неэффективно, потому что вы будете разветвлять awk много раз в цикле, один раз для каждого имени файла) или перепишите тот же алгоритм в perl и используйте библиотечный модуль IO :: Uncompress :: AnyUncompress , который может распаковывать несколько различных типов сжатых файлов (gzip, zip, bzip2 , lzop). или в python, в котором также есть модули для обработки сжатых файлов.


Вот версия perl , которая использует IO :: Uncompress :: AnyUncompress , чтобы разрешить любое количество шаблонов и любое количество имен файлов (содержащих простой текст или сжатый текст).

Все аргументы до - обрабатываются как шаблоны поиска. Все аргументы после - рассматриваются как имена файлов. Примитивная, но эффективная обработка опций для этой работы. Лучшая обработка параметров (например, поддержка параметра -i для поиска без учета регистра) может быть достигнута с помощью модулей Getopt :: Std или Getopt :: Long . .

Запустите его так:

$ ./arekolek.pl one two three -- *.gz *.txt
1.txt.gz
4.txt.gz
5.txt.gz
1.txt
4.txt
5.txt

(Я не буду перечислять здесь файлы {1..6} .txt.gz и {1..6} .txt ... они просто содержат некоторые или все слова "один" "два" "три" "четыре" "пять" и "шесть" для тестирования. Файлы, перечисленные в выходных данных выше, ДОЛЖНЫ содержать все три шаблона поиска.Проверьте это самостоятельно на своих данных)

#! /usr/bin/perl

use strict;
use warnings;
use IO::Uncompress::AnyUncompress qw(anyuncompress $AnyUncompressError) ;

my %patterns=();
my @filenames=();
my $fileargs=0;

# all args before '--' are search patterns, all args after '--' are
# filenames
foreach (@ARGV) {
  if ($_ eq '--') { $fileargs++ ; next };

  if ($fileargs) {
    push @filenames, $_;
  } else {
    $patterns{$_}=1;
  };
};

my $pattern=join('|',keys %patterns);
$pattern=qr($pattern);
my $p_string=join('',sort keys %patterns);

foreach my $f (@filenames) {
  #my $lc=0;
  my %s = ();
  my $z = new IO::Uncompress::AnyUncompress($f)
    or die "IO::Uncompress::AnyUncompress failed: $AnyUncompressError\n";

  while ($_ = $z->getline) {
    #last if ($lc++ > 100);
    my @matches=( m/($pattern)/og);
    next unless (@matches);

    map { $s{$_}=1 } @matches;
    my $m_string=join('',sort keys %s);

    if ($m_string eq $p_string) {
      print "$f\n" ;
      last;
    }
  }
}

Хэш % шаблонов содержит полный набор шаблонов, которые файлы должны содержать хотя бы по одному из каждого члена $ _ pstring - строка, содержащая отсортированные ключи этого хэша. Строка $ pattern содержит предварительно скомпилированное регулярное выражение, также построенное из хэша % patterns .

$ pattern сравнивается с каждой строкой каждого входного файла (с использованием модификатора / o для компиляции $ pattern только один раз, поскольку мы знаем, что он никогда не изменится во время выполнения), а map () используется для построения хэша (% s), содержащего совпадения для каждого файла.

Всякий раз, когда все шаблоны были замечены в текущем файле (путем сравнения if $ m_string (отсортированные ключи в % s ) равны $ p_string ]), напечатайте имя файла и перейдите к следующему файлу.

Это не особенно быстрое решение, но оно не является необоснованно медленным. Первая версия занимала 4 минуты 58 секунд на поиск трех слов в сжатых файлах журнала объемом 74 МБ (всего 937 МБ без сжатия). Текущая версия занимает 1 мин. 13 сек. Вероятно, можно было бы сделать дальнейшую оптимизацию.

Одна очевидная оптимизация заключается в использовании этого вместе с xargs -P aka - max-procs для выполнения нескольких поисков по подмножествам файлы параллельно. Для этого вам нужно подсчитать количество файлов и разделить на количество ядер / процессоров / потоков, которые есть в вашей системе (и округлить, добавив 1). напримерв моем наборе образцов выполнялся поиск 269 файлов, а в моей системе 6 ядер (AMD 1090T), поэтому:

patterns=(one two three)
searchpath='/var/log/apache2/'
cores=6
filecount=$(find "$searchpath" -type f -name 'access.*' | wc -l)
filespercore=$((filecount / cores + 1))

find "$searchpath" -type f -print0 | 
  xargs -0r -n "$filespercore" -P "$cores" ./arekolek.pl "${patterns[@]}" --

При такой оптимизации потребовалось всего 23 секунды, чтобы найти все 18 совпадающих файлов. Конечно, то же самое можно сделать и с любым другим решением. ПРИМЕЧАНИЕ. Порядок имен файлов, перечисленных в выходных данных, будет другим, поэтому, возможно, впоследствии потребуется их сортировка, если это имеет значение.

Как отмечает @arekolek, несколько zgrep с find -exec или xargs могут сделать это значительно быстрее, но этот сценарий имеет то преимущество, что поддерживает любое количество шаблонов для поиска и может работать с несколькими различными типами сжатия.

Если сценарий ограничен проверкой только первых 100 строк каждого файла, он проходит все из них (в моем образце размером 74 МБ из 269 файлов) за 0,6 секунды. Если это полезно в некоторых случаях, его можно преобразовать в параметр командной строки (например, -l 100 ), но при этом существует риск не найти всех совпадающих файлов.


Кстати, согласно странице руководства для IO :: Uncompress :: AnyUncompress , поддерживаются следующие форматы сжатия:


Последняя (надеюсь) оптимизация. Используя модуль PerlIO :: gzip (упакованный в debian как libperlio-gzip-perl ) вместо IO :: Uncompress :: AnyUncompress , я получил время примерно до 3.1 секунда для обработки моих 74 МБ файлов журнала. Были также некоторые небольшие улучшения за счет использования простого хеша вместо Set :: Scalar (что также позволило сэкономить несколько секунд с версией IO :: Uncompress :: AnyUncompress ).

PerlIO :: gzip был рекомендован как самый быстрый perl gunzip в https: // stackoverflow.com / a / 1539271/137158 (найдено с помощью поиска в Google по запросу perl fast gzip распаковать )

Использование xargs -P с этим нисколько не улучшило его . Фактически, казалось, что это даже замедлило его где-то от 0,1 до 0,7 секунды. (Я пробовал четыре запуска, и моя система выполняет другие функции в фоновом режиме, которые изменяют время)

Цена в том, что эта версия скрипта может обрабатывать только файлы, сжатые и сжатые в формате gzip. Скорость против гибкости: 3,1 секунды для этой версии против 23 секунд для версии IO :: Uncompress :: AnyUncompress с оболочкой xargs -P (или 1 мин. 13 с без xargs -P ).

#! /usr/bin/perl

use strict;
use warnings;
use PerlIO::gzip;

my %patterns=();
my @filenames=();
my $fileargs=0;

# all args before '--' are search patterns, all args after '--' are
# filenames
foreach (@ARGV) {
  if ($_ eq '--') { $fileargs++ ; next };

  if ($fileargs) {
    push @filenames, $_;
  } else {
    $patterns{$_}=1;
  };
};

my $pattern=join('|',keys %patterns);
$pattern=qr($pattern);
my $p_string=join('',sort keys %patterns);

foreach my $f (@filenames) {
  open(F, "<:gzip(autopop)", $f) or die "couldn't open $f: $!\n";
  #my $lc=0;
  my %s = ();
  while (<F>) {
    #last if ($lc++ > 100);
    my @matches=(m/($pattern)/ogi);
    next unless (@matches);

    map { $s{$_}=1 } @matches;
    my $m_string=join('',sort keys %s);

    if ($m_string eq $p_string) {
      print "$f\n" ;
      close(F);
      last;
    }
  }
}
13
20.08.2021, 12:04

Теги

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