печатать строки, где каждое слово содержит два одинаковых символа в Linux

печатать строки, где каждое слово содержит два одинаковых символа в Linux

У меня есть такой ввод

LTCYMM SVNNDA DTVEV QLOPGO CUPUR
MMWVJM LIVLI WBSVD UQCMW HBMDA HVVFY BWYSS
NOGWOS JIKKDI GCIQAD MXJNWE SMVFCB GIZVPA GZOHZR WJBMZS
KKPQBP BKDKRU ZTPDPL ZRLUEL HRZZKO KXSKCU YZQTBT RISNKS
VYQQC BFAWI NSZDV HKPGI KVJOC COPPS
JGU YLN MXW ACR BZA HOP
TMCVPT HBNGIH IQYGCI DTQPON WXANKG GMIYZS
CWVT BUBA NSGR MUPO LDNS

Я пытаюсь напечатать строки, где каждое слово содержитпо крайней мере два одинаковых персонажа, используя grep command Самая длинная строка содержит 8 слов , я думаю, что я могу решить это таким образом , но я чувствую, что это неправильный способ ,

grep '^.*\([A-Z]\)[^ ]*\1[^ ]* [^ ]*\([A-Z]\)[^ ]*\2[^ ]*   [^ ]*\([A-Z]\)[^ ]*\3[^ ]* [^ ]*\([A-Z]\)[^ ]*\4[^ ]* [^ ]*\([A-Z]\)[^ ]*\5[^ ]* [^ ]*\([A-Z]\)[^ ]*\6[^ ]* [^ ]*\([A-Z]\)[^ ]*\7[^ ]* [^ ]*\([A-Z]\)[^ ]*\8[^ ]*$/| .... for 7 words | for 6 ...

ожидаемый результат

 LTCYMM SVNNDA DTVEV QLOPGO CUPUR
 KKPQBP BKDKRU ZTPDPL ZRLUEL HRZZKO KXSKCU YZQTBT RISNKS

решение1

С perl:

$ perl -ne 'print unless grep {!/(.).*\1/} /\S+/g' file
LTCYMM SVNNDA DTVEV QLOPGO CUPUR
KKPQBP BKDKRU ZTPDPL ZRLUEL HRZZKO KXSKCU YZQTBT RISNKS

Или с grepреализациями с поддержкой регулярных выражений в стиле Perl:

$ grep -Pve '(?<!\S)(?!\S*(\S)\S*\1)\S' file
LTCYMM SVNNDA DTVEV QLOPGO CUPUR
KKPQBP BKDKRU ZTPDPL ZRLUEL HRZZKO KXSKCU YZQTBT RISNKS

Это печатает строки, которые делаютнет-v) содержат \S(непробельный символ), которому не предшествует другой непробельный символ ( (?<!\S)) (или IOW, который является началом слова, разделенного пробелом) и не является началом последовательности непробельных символов, один из которых повторяется ( (?!\S*(\S)\S*\1)). Так что по сути это похоже на (хотя и менее разборчиво) perlподход, описанный выше.

Обратите внимание, что они также печатают пустые строки (поскольку не содержат слов, в которых нет повторяющихся символов). Если они вам не нужны, вы можете исключить их, что должно быть тривиально (например, добавив a -e '^\s*$'в grepone).

решение2

Использование любого awk в любой оболочке на любой машине Unix:

awk '{
    for ( fldNr=1; fldNr<=NF; fldNr++ ) {
        numChars = length($fldNr)
        numUnq = 0
        split("",seen)       # you could use delete(seen) here in most awks
        for ( charNr=1; charNr<=numChars; charNr++ ) {
            if ( !seen[substr($fldNr,charNr,1)]++ ) {
                numUnq++
            }
        }
        if ( numUnq == numChars ) {
            next
        }
    }
    print 
}' file
LTCYMM SVNNDA DTVEV QLOPGO CUPUR
KKPQBP BKDKRU ZTPDPL ZRLUEL HRZZKO KXSKCU YZQTBT RISNKS

решение3

Используя perlвместе с allметодом из List::Utilмодуля мы можем обнаружить искомые строки (все слова с хотя бы одним повторяющимся символом)

perl -MList::Util=all  -lane '
  print if all { /(.).*\1/ } @F;
' file

Используя GnU sedмы можем выбрать нужные строки, убедившись, что все нужные поля простираются от начала строки до конца.

$ sed -En '/^\s*(\S*(\S)\S*\2\S*(\s+|$))+$/p' file

Другой способ sed— это постепенная проверка на наличие повторяющихся символов среди непробельных символов и не выводить шаблон сразу, как только не будет найдено ни одного повторяющегося символа в последовательности непробельных символов.

sed -Ee 'h
  :loop
    s/^\s+|\s+$//g
    s/\S+/&\n/
    /(\S).*\1.*\n/!d
    s/^[^\n]*\n//
  /./bloop
  g
' file

Мы используем awk, а затем циклически проходим по каждому слову и каждому символу в слове. Разбиваем слово по символу и проверяем, разбивается ли оно более чем на 2 части => в этом слове обнаружен дубликат. Аналогично в конце текущей строки, если количество обнаруженных дубликатов равно количеству полей => строка помещается для печати.

awk '
{
  for (p=i=1+(w=0); i<=NF; i++) {
    while (p <= length($i)) {
      c = substr($i,p++,1)
      if (split($i,a,c) > 2) {
        w += p = 1
        break
      }
    }
  }
}
w==NF
' file

решение4

Вот еще одно решение на чистом Bash — нет perl, нет grep, нет awk.

#!/bin/bash
set -euo pipefail

containssametwice() {
  local -Ai chars=()
  local -i i
  for ((i = 0; i < ${#1}; ++i)); do
    ((++chars["${1:i:1}"] < 2)) || return 0
  done
  return 1
}

while IFS= read -r line; do
  read -ra words <<< "$line"
  for word in "${words[@]}"; do
    containssametwice "$word" || continue 2
  done
  printf '%s\n' "$line"
done

Связанный контент