Извлечение нескольких строк из каждого файла в каталоге, редактирование текста и добавление имени файла для каждой совпадающей строки

У меня есть папка, содержащая несколько файлов, и из каждого файла мне нужно 1) извлечь все строки, содержащие элементы из заранее заданного списка слов; 2) добавить имя файла к этой строке 3) разделить извлеченную строку на две отдельные строки, удалив некоторое содержимое между ними.

Это содержимое файлов, из которых мне нужно извлечь:

freq +fchi +t*CHI +s"m|v"
Fri Feb 24 10:24:17 2017
freq (08-Jan-2016) is conducting analyses on:
  ONLY speaker main tiers matching: *CHI;
    and those speakers' ONLY dependent tiers matching: %MOR;
****************************************
From file <sarah004.cha>
Speaker: *CHI:
  2 v|eye-3S
  1 v|get
  2 v|get&PAST
  1 v|go&PAST
  1 v|help
  2 v|ride
  3 v|see
  1 v|toe-3S
------------------------------
    8  Total number of different item types used
   13  Total number of items (tokens)
0.615  Type/Token ratio

Пример списка слов, которые я ищу, - см. | Знать | смотреть (фактический список намного длиннее, около 25 слов).

В итоге я хочу получить следующее:

sarah004.cha    3    see
sarah006.cha    3    know
sarah010.cha    1    look
sarah010.cha    2    see
...

Итак, помимо извлечения строк, содержащих указанные слова, я бы также разделил число и слово и удалил v |.

Некоторые файлы в каталоге могут не содержать ни одного слова из списка, другие могут содержать многие слова из списка.

Я не уверен, что смогу сделать это с помощью sed; Я попробовал это в качестве отправной точки (просто извлекать любые строки со счетчиками), и он не завершился:

sed '/From file/{s/.*<\(.*\)>/\1/;h
}
/^  [0-9] v|/!d
s/.*= //;H;x' ./* | paste - - > sarahverbcounts.txt

Я бы хорошо сделал это в несколько шагов, хотя я уверен, что это можно сделать с помощью одной команды / сценария и я просто не знаю как. Я думаю, что Perl справится с этим, но я не знаю синтаксиса (я пробовал использовать найденный мной пример perl и не мог понять, как правильно изменить его для своих целей).Возможно, мне нужно попробовать это в Python, что позволило бы мне указать список слов, но я не знаком с тем, как заставить Python перебирать все файлы в каталоге один за другим и все они записываются в один и тот же выходной файл ( Я просмотрел примеры, но я их не совсем понимаю). Любые советы приветствуются (и, если возможно, объяснения того, как работает ваше решение, были бы замечательными, потому что я достаточно новичок в этом, я не всегда могу разбирать полезные ответы, которые мне дают).

-1
24.02.2017, 19:09
3 ответа

Поместите список слов для поиска в файл с именем words.txt а затем запустите:

grep -Hf words.txt files.* | sed 's/:/ /;s/v|/ /'
1
28.01.2020, 05:10

Вы можете сделать это разными способами, как показано. Шаблоны сохраняются по одному на строку в файле списка.

Примечание. Мы отличаем файл шаблонов от остальных файлов данных (предположительно, ваших файлов * cha ) с помощью различных уловок в bash, perl и sed. В случае "sed" шаблоны хранятся в точечном файле, также известном как скрытые файлы, & с добавлением "." чтобы помочь коду "sed" отличить файл шаблона от обычного файла данных. Это WA для sed, поскольку он не имеет, как в awk, концепции "FNR".

Grep

grep -E -f ./.your_patterns_listfile ./* |
sed -e 's/://' -e 's/v|//'

Perl

find . -maxdepth 1 -type f -name '*' -exec \
   perl -wMstrict -Mvars='*pat' -lne '
      BEGIN { ($pat) = @ARGV; }
      if ( $pat ne $ARGV ) {
         @pat = map { quotemeta } keys %pat unless @pat;
         next unless /^From file [<]/ .. /^--*/;
         my $cha;
         /^From file [<]([>]*)[>]/ and $cha = $1;
         for my $pat ( @pat ) {
            /^\s+ (\d+) \s+ v[|] ($pat) $/x and print(join("\t",$ARGV,$1,$2)),last
         }
      } else {
         $pat{$_}++;
      }
   ' ./your_patterns_listfile {} +

Sed

echo '.' >> ./.your_patterns_listfile
find . -maxdepth 1 -type f -name '*' -exec \
   sed -e '
      1{
         :pats
            N
         /\n\.$/!bpats
         s///;h;d
      }

      /^From file </!d
      $d;N
      /\n[ ]*\([1-9][0-9]*\)[ ]v|/{
         s//\n\1\t|/;s/$/|/
         G;s/\n/&&/;s/$/\n/
         /\n\n[1-9][0-9]*\t|\([^|]*\)|.*\n\1\n/{
            s/^From file <\([^>]*\)>\n\n\([1-9][0-9]*\)\t|\([^|]*\)|/\1\t\2\t\3\n&/
            P;s/\n\n.*//;D
         }
      }
      s/\n.*//;s/^/\n/;D
   ' ./.your_patterns_listfile {} +

Bash

find . -type f -name '*' ! -name 'your_patterns_listfile' -exec \
   sh -c '
      shift $1
      flag=
      eval "`echo '\''readonly NL=qsq'\'' | tr '\''qs'\'' '\''\047\012'\''`"; # newline
      pats=$(< $1); shift
      cat "$@" |
      while IFS= read -r line
      do
         case $line in
            "From file <"* )
               cha=${line#*"<"}
               cha=${cha%">"}
               unset flag
               continue
               ;;

            *[0-9]*v\|* )
               pat=${line#*"|"}
               num=${line%%"v"*}
               num=${num//[ ]/}
               case $pats in
                  "$pat" | *"$NL$pat$NL"* | "$pat$NL"* | *"$NL$pat" )
                     ${flag+":"} printf "%s\t" "$cha" "$num" "$pat"
                     ${flag+":"} echo
                     ;;
               esac
               ;;

            '' | *[!-]* )
               :
               ;;

            * )
               flag=
               ;;
         esac
      done
   ' 2 1 ./your_patterns_listfile {} +
1
28.01.2020, 05:10

Может быть что-то вроде этого:

egrep '^\s+[0-9]+\s+v\|([0-9a-zA-Z-]+)$' files* | sed -e 's/v|//g'
-1
28.01.2020, 05:10

Теги

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