awk динамическое сопоставление строк

Судя по всему, браузер (в моем случае, Google Chrome ), «удерживал» мультимедийные клавиши, когда что-то проигрывал. Даже без браузерного носителя ключи были выделены только для Chrome.

Одно из возможных решений:

  1. Paste chrome ://flags/ #hardware -media -key -обработка в Chrome;
  2. В первом варианте выберите «Отключить»;
  3. Откройте Spotify и наслаждайтесь!
2
13.05.2021, 22:58
3 ответа

Использование GNU awk для массивов массивов:

$ cat tst.awk
NR==FNR {
    file = $NF
    name = $1 (NF>2 ? " " $2 : "")
    if ( !(file in file2names) && ((getline line < file) > 0) ) {
        close(file)
        ARGV[ARGC++] = file
    }
    file2names[file][name]
    next
}
{
    $0 = " " $0 " "
    for (name in file2names[FILENAME]) {
        if ( pos = index($0," "name" ") ) {
            split(substr($0,1,pos),bef)
            split(substr($0,pos+length(name)+1),aft)
            print name, bef[1], bef[2], aft[1], aft[2]
        }
    }
}

$ awk -f tst.awk file
Ito path of etmlmbimiiit tlmmgli

Если вы действительно хотите, чтобы все строки имени файла до -из «файла» были частью имени, а не только первые 1 или 2 строки (, см. комментарии ниже ), тогда просто измените:

name = $1 (NF>2 ? " " $2 : "")

к этому в gawk:

name = gensub(/\s+\S+$/,"",1)

или это в любом awk:

name = $0
sub(/ +[^ ]+$/,"",name)

В любом другом awk вы просто сохраните имена файлов в виде строки, -разделенной пробелом, например. вместо file2names[file][name]вы бы сделали file2names[file] = (file in file2names ? file2names[file] FS : "") name, а затем разделили их перед циклом, например. вместо for (name in file2names[file])вы бы сделалиsplit(file2names[FILENAME],names); for (name in names)

Вышеприведенный ввод file— это только первый файл в вашем примере.

2
28.07.2021, 11:32

Данные входные файлы:

$ cat first.file
Ito 65482.txt
David Juno Ilrcwrry Hold 73586.txt
David Jones 73586.txt
Jacob FleUchbautr 73586.txt

$ cat 65482.txt
nose just brnukiiitt tip tinwallfin the golden
path of Ito etmlmbimiiit tlmmgli the trees
Butt It as tie not intra and plcturosiiiicness
limit wo were of m that is not altogether We
and hunting and llslilng In plenty anti lit lIly

$ cat 73586.txt
Lorem ipsum David Jones dolor sit amet, consectetur adipiscing elit. Curabitur non ultrices tellus. Donec porttitor sodales mattis. Nulla eu ante eget libero dictum accumsan nec non odio. Nullam lobortis porttitor mauris a feugiat. Vestibulum ultrices ipsum at maximus consequat. Vivamus molestie Jacob FleUchbautr tortor ac felis varius gravida. Cras accumsan dolor at velit sodales auctor. Vestibulum sit amet scelerisque eros, quis porta orci. Donec eget erat dolor. Integer id vestibulum massa. Quisque lacus risus, venenatis nec euismod nec, ultrices sed mi. Proin tincidunt ipsum mattis lectus pulvinar interdum. Suspendisse convallis justo iaculis, semper nisl at, imperdiet ante.
#..........^^^^^^^^^^^..................................................................................................................................................................................................................................................................................^^^^^^^^^^^^^^^^^

, затем:

mapfile -t files < <(awk '{print $NF}' first.file | sort -u)

word='[^[:blank:]]+'

for file in "${files[@]}"; do
    mapfile -t names < <(grep -wF "$file" first.file | sed -E 's/ [^ ]+$//')
    pattern="($word $word) ($(IFS='|'; echo "${names[*]}")) ($word $word)"
    declare -p file pattern
    grep -oE "$pattern" "$file" | sed -E "s/$pattern/\\2 \\1 \\3/"
done

выходы

declare -- file="65482.txt"
declare -- pattern="([^[:blank:]]+ [^[:blank:]]+) (Ito) ([^[:blank:]]+ [^[:blank:]]+)"
Ito path of etmlmbimiiit tlmmgli
declare -- file="73586.txt"
declare -- pattern="([^[:blank:]]+ [^[:blank:]]+) (David Juno Ilrcwrry Hold|David Jones|Jacob FleUchbautr) ([^[:blank:]]+ [^[:blank:]]+)"
David Jones Lorem ipsum dolor sit
Jacob FleUchbautr Vivamus molestie tortor ac

Это регулярное выражение требует, чтобы 2 слова отображались до и после имени. Если имя появляется в начале или в конце строки, совпадения нет.

1
28.07.2021, 11:32

Это можно сделать в awk, но IMO гораздо проще сделать в perl. И это еще до того, как вы учтете, что в Lingua::*существует более 800 библиотечных модулей perl для различных задач обработки естественного языка, чем вы, похоже, и занимаетесь.

Следующий perl-скрипт сначала строит часто используемую структуру данных perl, называемую Hash -of -Arrays (HoA ), используя имена файлов в качестве ключей для ассоциативного массив (, также известный как hash), и значения каждого ключа , являющиеся индексированным массивом имен. См. man perldscдля получения дополнительной информации о HoA и других структурах данных perl.

В итоге %filesАП будет содержать такие данные, как:

{
  "65482.txt" => ["Ito"],
  "73586.txt" => ["David Juno Ilrcwrry Hold", "David Jones", "Jacob FleUchbautr"],
}

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

Если имя файла не существует, выводится предупреждающее сообщение в STDERR и выполняется переход к следующей строке «первого» файла. Строку print STDERR...можно удалить или закомментировать, если вы не хотите получать предупреждения, или просто перенаправить stderr в /dev/null при запуске.

По завершении построения %filesHoA он открывает каждый файл для чтения, создает и предварительно -компилирует регулярное выражение, соответствующее любому из имен, требуемых для этого конкретного файла, и печатает каждую строку, соответствующую RE.

Регулярные выражения, которые он строит, заканчиваются такими значениями, как:

(((\w+\s+){2})(David Juno Ilrcwrry Hold|David Jones|Jacob FleUchbautr)((\s+\w+){2}))

Причина этого заключается в том, что каждое имя файла должно быть обработано только один раз , и каждая строка каждого файла должна быть проверена только один раз, чтобы увидеть, соответствует ли она одному из имен. Если у вас много файлов и/или если они очень большие,это приводит к огромному повышению производительности по сравнению с наивным подходом многократного чтения и сопоставления каждой строки каждого файла, один раз для каждого имени, указанного в «первом» файле -, например. если бы у вас было 1000 файлов по 1000 строк в каждом и всего 50 имен для сопоставления, наивный метод должен был бы прочитать и сопоставить строку 50 миллионов раз (файлов *строк *имен )вместо всего 1 миллион раз (файлов *строк)

Сценарий настроен таким образом, чтобы упростить выбор способа сопоставления слов до и после совпадающего имени. Раскройте -комментарий только одной из двух my $count=строк сценария. Первый строго требует ровно два слова перед И после каждого имени -, это уже не -закомментировано. Во втором смягчен вопрос о том, сколько слов может существовать до или после имени (от 0 до 2 ).

#!/usr/bin/perl -l

use strict;
my %files = ();
my @order = ();

# Un-comment only one of the following two lines.
my $count=2;
#my $count='0,2';

# First, build up a HoA where the key is the filename and
# the value is an array of names to match in that file.
while(<>) {
  s/^\s+|\s+$//;   # strip leading and trailing spaces
  next if (m/^$/); # skip empty lines
  my ($name,$filename) = m/^(.*)\s+(.*)$/; # extract name and filename

  # warn about and skip filenames that don't exist
  if (! -e $filename) {
    print STDERR "Error found on $ARGV, line $.: '$filename' does not exist.";
    next;
  };

  # remember the order we first see each filename.
  push @order, $filename unless ( defined($files{$filename}) );

  # Add the name to the %files HoA
  push @{ $files{$filename} }, $name;
};

# Process each file once only, in order.
foreach my $filename (@order) {
  open(my $fh,"<",$filename) || die "Error opening $filename for read: $!\n";

  my $re = "(((\\w+\\s+){$count})(".           # two words
           join('|',@{ $files{$filename} }).   # the names
           ")((\\s+\\w+){$count}))";            # and two words again

  $re = qr/$re/;  # add an 'i' after '/' for case-insensitive

  while(<$fh>) {
    if (m/$re/) {
      my $found = join(" ",$4,$2,$5);
      $found =~ s/\s\s+/ /g;
      print $found
    };
  };
}

Сохранить как, например. match.plи сделайте исполняемый файл с помощью chmod +x match.plи запустите как:

$./match.pl first.txt 
Error found on first.txt line 2: '73586.txt' does not exist.
Error found on first.txt line 3: '73586.txt' does not exist.
Error found on first.txt line 4: '73586.txt' does not exist.
Ito path of etmlmbimiiit tlmmgli

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

Ito:path of etmlmbimiiit tlmmgli

Вы можете сделать это, изменив строку my $found = на:

my $found = "$4:". join(" ",$2,$5);

или

my $found = "$4\t". join(" ",$2,$5);
1
28.07.2021, 11:32

Теги

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