Как вывести количество вхождений согласных и гласных для каждого файла с помощью awk?

Здесь есть разные проблемы. Прежде всего, выражение ^[ ]wозначает :найти начало строки, затем ровно один пробел, затем w. Так что на самом деле работает отлично. Если вы хотите, чтобы он соответствовал одному или нескольким пробелам, вам нужно добавить классификатор к классу символов [ ]:

  $ grep '^[  ]\+w' text.txt
 whitespace 1
  whitespace 2

+означает «один или несколько». Вариант регулярных выражений по умолчанию, используемый grep, называется базовым регулярным выражением BRE (), и в этом варианте регулярного выражения +необходимо экранировать, поэтому \+выше*. Кроме того, вы можете использовать расширенные регулярные выражения ERE (), передав флаг -E, или PCRE (Perl-совместимые регулярные выражения ), передав флаг -P. С этими вариантами регулярных выражений вам не нужно экранировать +, чтобы он действовал как квантификатор:

$ grep -P '^[  ]+w' text.txt
 whitespace 1
  whitespace 2
$ grep -E '^[  ]+w' text.txt
 whitespace 1
  whitespace 2

Следующая и более важная проблема заключается в том, что вы не цитируете регулярное выражение. Заключение в кавычки необходимо для того, чтобы регулярное выражение передавалось вgrepкак есть , а не сначала интерпретировалось оболочкой. Однако, поскольку вы не цитируете его, он расширяется оболочкой до того, как будет передан grep. Вы можете проверить это, используя опцию set -x, чтобы оболочка распечатала, что она делает:

$ set -x
$ grep ^[ ]{0,}w text.txt
+ grep '^[' ']0w' ']w' text.txt
grep: Invalid regular expression

Во-первых, поскольку между ^[и ]есть пробел, оболочка интерпретирует это как два отдельных аргумента:^[и ]{0,}w. Но {}используются в оболочке для расширения скобок. Например:

$ echo foo{a,b}
fooa foob

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

$ echo foo{a,}
fooa foo

Таким образом, расширение ]{0,}wстановится:

$ echo ]{0,}w
]0w ]w

И в результате, и как вы можете видеть в выводе set -xвыше, эти три аргумента фактически передаются вgrep:

'^[' ']0w' ']w'

Но если вы их цитируете, их нужно экранировать при использовании BRE,точно так же, как +выше:

$ grep '^[ ]\{2\}w' text.txt
  whitespace 2

Последнее замечание :[ ]точно такое же, как , нет смысла использовать класс символов для одного символа.

Объединяя все это, чтобы соответствовать ровно одному пробелу в начале строки, используйте:

$ grep '^ w' text.txt 
 whitespace 1

Чтобы сопоставить один или несколько, используйте:

$ grep '^ \+w' text.txt 
 whitespace 1
  whitespace 2

Или:

$ grep -E '^ +w' text.txt 
 whitespace 1
  whitespace 2

или

$ grep -P '^ +w' text.txt 
 whitespace 1
  whitespace 2

Для соответствия определенному диапазону номеров (, например. 0, 1 или 2 пробела):

$ grep '^ \{0,3\}w' text.txt 
whitespace 0
 whitespace 1
  whitespace 2

или

$ grep -P '^ {0,3}w' text.txt 
whitespace 0
 whitespace 1
  whitespace 2

или

$ grep -E '^ {0,3}w' text.txt 
whitespace 0
 whitespace 1
  whitespace 2

И чтобы соответствовать определенному номеру, либо установите этот номер в {}, как показано выше, либо просто повторите символ N раз:

$ grep '^ \{2\}w' text.txt
  whitespace 2
$ grep '^ w' text.txt
 whitespace 1
$ grep '^  w' text.txt
  whitespace 2

Ивсегда цитируйте ваши регулярные выражения!


*На самом деле, в POSIX BRE +не имеет специального значения, но BRE, реализованный GNU grep, распознает его, если он экранирован.

0
27.03.2021, 16:15
1 ответ

Чтобы ответить на этот дополнительный вопрос , вот мой дополнительный ответ с GNU awk (и измененный теперь, чтобы считать только b, c, d и т. д. как не -гласные вместо каждого символа, который не является aeiou, например Àи é, упомянутые @StéphaneChazelas в комментарии):

$ awk -v IGNORECASE=1 '
    {
        v_cnt += gsub(/[aeiou]/,"")
        c_cnt += gsub(/[bcdfghjklmnpqrtsvwxyz]/,"")
    }
    ENDFILE {
        print FILENAME, v_cnt+0, c_cnt+0
        v_cnt = c_cnt = 0
    }
' file1 file2
file1 5 12
file2 2 7

Я оставлю это как простое упражнение по изменению эквивалента POSIX awk из моего предыдущего ответа.

Если вам также нужно какое-то указание на наличие буквенных символов, не перечисленных ни в одном из приведенных выше выражений в квадратных скобках, то это просто настройка:

awk -v IGNORECASE=1 '
    {
        v_cnt += gsub(/[aeiou]/,"")
        c_cnt += gsub(/[bcdfghjklmnpqrtsvwxyz]/,"")
    }
    /[[:alpha:]]/ {
        gsub(/[^[:alpha:]]+/,"")
        printf "Warning %s[%d]: Unexpected chars found: %s\n", FILENAME, FNR, $0 > "/dev/stderr"
    }
    ENDFILE {
        print FILENAME, v_cnt+0, c_cnt+0
        v_cnt = c_cnt = 0
    }
' file1 file2

То, как это обрабатывается, конечно, можно трактовать по-разному и с различными количествами + деталями вывода.

4
28.04.2021, 22:56

Теги

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