imprimir linhas onde cada palavra contém dois caracteres iguais no Linux

imprimir linhas onde cada palavra contém dois caracteres iguais no Linux

Eu tenho uma entrada assim

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

estou tentando imprimir linhas onde cada palavra contémpelo menos dois mesmos personagens, usando grep command A linha mais longa contém 8 palavras, acho que posso resolver assim, mas sinto que é o caminho errado,

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 ...

resultado esperado

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

Responder1

Com perl:

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

Ou com grepimplementações com suporte para expressões regulares do tipo Perl:

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

Isso imprime as linhas que fazemnão(com -v) contém um \S(caractere sem espaço em branco) que não é precedido por outro que não seja espaço em branco ( (?<!\S)) (ou IOW que é o início de uma palavra delimitada por espaço em branco) e não é o início de uma sequência de espaços sem espaço em branco dos quais é repetido ( (?!\S*(\S)\S*\1)). Portanto, em essência, semelhante (embora menos legível que) à perlabordagem acima.

Observe que eles também imprimem linhas em branco (pois não contêm palavras que não possuam caracteres repetidos). Se você não os quiser, poderá excluí-los, o que deve ser trivial (como adicionar um -e '^\s*$') grep.

Responder2

Usando qualquer awk em qualquer shell em cada caixa 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

Responder3

Usando perljunto com o allmétodo do List::Utilmódulo podemos detectar as linhas desejadas (todas as palavras com pelo menos um caractere duplicado)

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

Usando GnU sedpodemos selecionar as linhas desejadas quando garantirmos que todos os campos desejados se estendam do início ao fim da linha.

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

Outra maneira sedé verificar progressivamente se há um caractere duplicado entre os caracteres que não são espaços em branco e não imprimir o espaço padrão assim que nenhuma duplicata for encontrada em uma série de caracteres que não sejam espaços em branco.

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

Utilizamos o awk e, em seguida, percorremos cada palavra e cada caractere de uma palavra. Divida a palavra sobre o caractere e verifique se ela se divide em mais de 2 partes => dup detectado nesta palavra. Da mesma forma, no final da linha atual, se a contagem de duplicações detectadas for igual ao número de campos => linha adequada para impressão.

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

Responder4

Aqui está outra solução em Bash puro — no perl, no grep, no 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

informação relacionada