Точное количество одинаковых символов в слове-grep

Не прямой ответ на ваш вопрос, но вы можете проверить

$ ls -l /dev/serial/by-id/

(после подключения хотя бы одного последовательного USB-адаптера -), чтобы проверить, поможет ли это решить вашу проблему.

Вы также можете проверить /lib/udev/rules.d/60-serial.rules, чтобы получить пример и посмотреть, как работает /dev/serial. На моем Kubuntu 16.04 это выглядит так:

ACTION=="remove", GOTO="serial_end"
SUBSYSTEM!="tty", GOTO="serial_end"

SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"

# /dev/serial/by-path/, /dev/serial/by-id/ for USB devices
KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end"

SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}"

IMPORT{builtin}="path_id"
ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="serial/by-path/$env{ID_PATH}"
ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-path/$env{ID_PATH}-port$env{.ID_PORT}"

IMPORT{builtin}="usb_id"
ENV{ID_SERIAL}=="", GOTO="serial_end"
SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACE_NUM}="$attr{bInterfaceNumber}"
ENV{ID_USB_INTERFACE_NUM}=="", GOTO="serial_end"
ENV{.ID_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}"
ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}"

LABEL="serial_end"
3
02.09.2019, 03:41
3 ответа

Следующее соответствует любому «слову» в начале строки, состоящей только из 3 одинаковых [ :альфа :] символов:

grep -i '^\([[:alpha:]]\)\1\1\b' 

Или с опциями grep-E(--extended-regexp)или -P(, также известными как--perl-regexp):

grep -iE '^([[:alpha:]])\1\1\b'

grep -iP '^([[:alpha:]])\1\1\b'

Они работают с GNU grep и (, за исключением версии -P)с grep FreeBSD. Они могут не работать с другими версиями grep.


Если вы хотите сопоставить слова любой длины, содержащие 3 или более одинаковых буквенных символов в любом месте, это немного сложнее. Вам нужно использовать отрицательный просмотр вперед , который требует Perl-совместимых регулярных выражений.

т. е. это нельзя сделать сgrep -E(ака egrep, который устарел).

Например:

$ grep -iP '^[[:alpha:]]*([[:alpha:]])((?:(?!\1)[[:alpha:]])*\1){2}[[:alpha:]]*\b' /usr/share/dict/words
Aaliyah
Aaliyah's
Aarau
Aargau
Aaronical
Abadan
Abbottstown
Abbottstown's
Aberdeen
Aberdeen's
...
zoozoo
zoozoos
zuzzes
zwitterionic
zygogeneses
zygomorphous
zymogeneses
zyzzyva
zyzzyvas
zzz

(согласно wc -l, это соответствует 67117 из 344817 слов в моем файле /usr/share/dict/words)


И, наконец, сопоставлять только слова с ровно 3 одинаковых [ :альфа :] где-либо внутри них:

$ grep -iP '^[[:alpha:]]*([[:alpha:]])((?:(?!\1)[[:alpha:]])*\1){2}[[:alpha:]]*\b' /usr/share/dict/words | 
  grep -viP '^[[:alpha:]]*([[:alpha:]])((?:(?!\1)[[:alpha:]])*\1){3}'

Первый grep находит слова с 3 или более одинаковыми символами, а второй исключает слова с 4 или более.

Я не уверен, можно ли это сделать с помощью одного регулярного выражения или нет.

(это соответствует 56820 словам в моем файле /usr/share/dict/words ).

-1
27.01.2020, 21:29

Не нет способа построить такое регулярное выражение, используя только ERE (расширенное регулярное выражение ).

Ближе с GNU grep (perl regex )(, которые соответствуют 3 или более повторяющимся символам ), будет:

grep -P '(\w)(((?!\1)\w)*\1){2}' filename

Итак, удаляя слова с 4 и более повторами, вы получите ответ:

grep -P      '(\w)(((?!\1)\w)*\1){2}' filename | 
    grep -Pv '(\w)(((?!\1)\w)*\1){3}'

Альтернативой GNU awk является:

awk '{
      a=$1;
      while (length(a)){
                        b=gensub(substr(a,0,1),"","g",a);
                        if(length(a)-length(b)==3){print $0;next};
                        a=b
                       }
     }' filename

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

Предположим, что вы хотите считать Aэквивалентом a, затем отфильтруйте файл с помощью:

cat /usr/share/dict/words | tr [[:upper:]] [[:lower:]] > words

Два решения похожи, но не равны. Они отличаются такими словами, как independenceиз файла словаря, сгенерированного выше.

Да, independenceсодержит 3 n, но 4 e. В зависимости от того, какое слово найдено первым, слово может быть включено или нет. Решение Awk стабильно и будет включать слова, в которых любой символ повторяется ровно 3 раза.Решение с регулярным выражением более скользкое и будет соответствовать в одних условиях, а не в других.

Кроме того, регулярное выражение будет соответствовать только слову символам, которые не включают'(и файл содержит несколько слов с этим символом ).

Всего совпавших строк (на 1527 больше с awk):

 13758 awklist
 12231 greplist

И удаление еще'(184 с помощью awk):

 9236 awklist2
 9052 greplist2

Следует лиtastelessness teleconferencing teletypewriter teletypewriters tempestuousness timelessness tintinnabulation tintinnabulations tirelessness transcontinental transgressors transubstantiation(просто перечислить несколько )и отклонить?

У всех есть ровно 3 одного символа и четыре (или более )другого.

0
27.01.2020, 21:29

Я не думаю, что вы сможете сделать это с помощью grepи регулярных выражений, даже с функциями perl/pcre, такими как утверждения нулевой -длины и обратные ссылки.

Скорее всего, это какая-то теоретическая кроличья нора, но я не в ладах с такими вещами.

Так что просто сделайте это на Perl. «Алгоритм» можно легко перевести на awk, ruby, python и т. д. :

.
perl -CiI -anle 'my ($i,%l); ($n=++$l{$_})==3 ? $i++ : $n==4 ? $i-- : () for $F[0]=~/\pL/g; print if $i' file

Это можно легко адаптировать. Например, если вы хотите найти слова, в которых 3 буквы повторяются 3 раза:

perl -CiI -anle 'my ($i,%l); ($n=++$l{$_})==3 ? $i++ : $n==4 ? $i-- : () for $F[0]=~/\pL/g; print if $i >= 3' /usr/share/dict/words
...
entertainment
...
totalitarianism

или 7 букв, повторенных 2 раза:

perl -CiI -anle 'my ($i,%l); ($n=++$l{$_})==2 ? $i++ : $n==3 ? $i-- : () for $F[0]=~/\pL/g; print if $i >= 7' /usr/share/dict/words
...
electroencephalograph
...
telecommunication

Вы также можете изменить \pLна просто .для соответствия любой букве, $F[0]=~/../только на /../и без переключателя -aдля соответствия всей строке, опустить -CiI, чтобы учитывать только буквы ascii, и т. д.

1
27.01.2020, 21:29

Теги

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