Извлечение полей и подстрок и объединение отсортированных строк

pdfid, по-видимому, плохо справляется с подсчетом пар obj/endobj --в вашем конкретном примере нечетный «obj» является частью потока FlateDecode:

$ cat pdf.pl
use Compress::Zlib qw(inflateInit Z_STREAM_END);
use strict;
my ($o);
while(<>){
        $o -= s/\bendobj\b//g;
        $o += s/\b\d+\s+\d+\s+obj\b//g;
        if(/\bstream\s*$/){
                local $/ = "endstream"; my $s = <>; $s =~ s/\s*endstream$//;
                if($s =~ /(\w*obj)/){
                        my ($d, $err) = inflateInit->inflate($s);
                        if(length($s) == 0 && $err == Z_STREAM_END){
                                warn "innocuous '$1' in well formed stream\n";
                        }else{
                                warn "WARNING: inflateInit: $err\n";
                        }
                }
        }
        if(/(\w*obj)\b/){ warn "WARNING: possible stray $1\n" }
}
warn "WARNING: unbalanced obj/endobj: $o\n" if $o;
$ perl pdf.pl using-freedos-24.pdf
innocuous 'obj' in well formed stream

Обратите внимание :, что мало что может проиллюстрировать рассматриваемую проблему; не используйте его для проверки безопасности PDF-файла;-)

Формат pdf довольно неприятный и сложный; вам действительно нужен -полноценный синтаксический анализатор, чтобы понять его структуру. И программа, которая способна это делать (для правильной идентификации вредоносных PDF-файлов ), сама становится вектором атаки --нет причин полагать, что специальный -синтаксический анализатор более безопасен, чем libpoppler или либмупдф.

3
14.06.2021, 02:24
4 ответа

Использование awkи GNUdatamash:

awk 'BEGIN{ OFS=FS="\t" }
  NR>2{                       # skip first two records
    split($3, a, "/" )        # split $3 into array a on /
    domain=a[3]               # 3rd element is the domain name
    sub(/^www\./, "", domain) # remove www. prefix
    print domain, $4          # print domain and email
  }
' file | datamash -g 1 unique 2

Часть awkпечатает домен и адрес электронной почты для всех записей, пропуская первые две строки. Это будет

a.com   a@b.com
a.com   a@b.com
b.fr    c@hl.com
b.fr    a@hl.com

Затем выходные данные направляются в datamashдля группировки ввода по первому полю и вывода разделенного запятыми -списка уникальных значений второго поля.

Выход:

a.com   a@b.com
b.fr    a@hl.com,c@hl.com

Строка заголовка оставлена ​​в качестве упражнения.

4
28.07.2021, 11:24

попробуй:

cat inputfile |
awk '{
    match($3, "^https?://.*\\.([^./]+\\.[^./]+)", matches);
    print(matches[1], $4)
}' |
sort |
uniq |
awk '{
    domains[$1] = domains[$1] " " $2;
}
END {
    for (d in domains) {
        printf("%-15s ", d);
        $0 = domains[d];
        for (i = 1; i < NF; i++) {
            printf("%s, ", $i);
        };
        print($NF);
    };
}'

Первый awk соответствует завершающему компоненту домена foo.bar, за которым следует электронная почта. Фильтрация по sort | uniqустраняет повторяющиеся комбинации домен/электронная почта и гарантирует, что результаты находятся в порядке домена -, но нам все еще нужно объединить поле домена и объединить совпадающие электронные письма с запятыми, поэтому второй awk просто продолжает добавлять электронные письма в словарь с ключом домена (с использованием конкатенации с пробелом в -между ). Затем, в конце, мы перебираем все электронные письма и печатаем их с запятой в конце, если только это не последний домен, где запятая не нужна, поэтому мы просто делаем второй -до -последним итерации -. ] остановить и в заключение вывести последнее поле ($NF).

Я пытался использовать один awk-скрипт с двухмерным -мерным array[domain][email], но не продвинулся очень далеко, потому что я думаю, что они только симулируются в awk, т.е. с awk for (d in domain) { for (e in d[email]) }это невозможно. Поэтому я прибегнул к шелловому фильтру.

Мне также не нравятся хакерские циклы конкатенации строк, которые становятся нелепыми при большом количестве электронных писем в домене. Но это должно сработать, если у вас всего тысячи;для миллионов следует использовать лучший метод и т. д.

Я произвольно выбрал 15-символьное поле для домена. Вы можете удалить длину поля, если вам не нужно, чтобы они были выровнены. Вам понадобится печать BEGIN {}, если вы хотите распечатать строку заголовка...

вывод выглядит так, учитывая ваши ответы на вопрос:

a.com           a@b.com
b.fr            a@hl.com, c@hl.com

(с добавлением фиктивных данных для полей 1 и 2, которые вы оставили пустыми; эти поля должны содержать фактические данные без пробелов, чтобы скрипт работал, в противном случае вам понадобится разделитель полей, например табуляция или запятая, во входном файле)

1
28.07.2021, 11:24

Использование Миллера(с небольшой помощью sed):

$ mlr --prepipe 'sed "/^$/d"' --tsv   put -q -S '
  $domain = joinv(mapexcept(splitnvx(joinv(mapselect(splitnvx($URL,"/"),3),""),"."),1),".");
  @e[$domain] = mapsum(@e[$domain],{$email:1});
  end {
    for(k,v in @e){@{email(s)}[k] = joink(v,",")};
    emit @{email(s)}, "domain"
  }' File.tsv
domain  email(s)
a.com   a@b.com
b.fr    c@hl.com,a@hl.com

Команда sed --prepipeпросто удаляет лишнюю пустую строку, чтобы ввод можно было проанализировать как TSV. Переменная $domainполучается путем разделения поля URLдважды, сначала на/(выбирая 3-й элемент ), затем на.(выбирая все, кроме 1-го элемента, например.www). Затем исходящая -из -карта потоков @eстроится как карта полей email-. Это шаг, на котором de -дублирует электронные письма для одного и того же домена.. В endпреобразуйте карты электронной почты в строки с разделителями-запятыми -и отправьте их.

2
28.07.2021, 11:24

С помощью GNU awk для массивов массивов иgensub():

$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR>1 { d2e[gensub(/[^.]+\.([^.]+\.[^./]+).*/,"\\1",1,$3)][$4] }
END {
    print "domain", "emails(s)"
    for (domain in d2e) {
        cnt = 0
        for (email in d2e[domain]) {
            row = (cnt++ ? row ", " : domain OFS) email
        }
        print row
    }
}

$ awk -f tst.awk file
domain  emails(s)
a.com   a@b.com
b.fr    c@hl.com, a@hl.com
3
28.07.2021, 11:24

Теги

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