Здесь есть разные проблемы. Прежде всего, выражение ^[ ]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
, распознает его, если он экранирован.
Чтобы ответить на этот дополнительный вопрос , вот мой дополнительный ответ с 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
То, как это обрабатывается, конечно, можно трактовать по-разному и с различными количествами + деталями вывода.