imprimir líneas donde cada palabra contiene dos caracteres iguales en Linux

imprimir líneas donde cada palabra contiene dos caracteres iguales en Linux

tengo entradas como esta

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

Estoy intentando imprimir líneas donde contiene cada palabra.al menos dos personajes iguales, usando grep command La línea más larga contiene 8 palabras, creo que puedo resolverlo así, pero creo que es la forma incorrecta.

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

Rendimiento esperado

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

Respuesta1

Con perl:

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

O con grepimplementaciones compatibles con expresiones regulares tipo Perl:

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

Eso imprime las líneas que hacenno(con -v) contiene un \S(carácter que no es un espacio en blanco) que no está precedido por otro que no es un espacio en blanco ( (?<!\S)) (o IOW que es el comienzo de una palabra delimitada por espacios en blanco) y no es el comienzo de una secuencia de una palabra que no es un espacio en blanco del cual se repite ( (?!\S*(\S)\S*\1)). En esencia, es similar (aunque menos legible) al perlenfoque anterior.

Tenga en cuenta que también imprimen líneas en blanco (ya que no contienen palabras que no tengan caracteres repetidos). Si no los desea, puede excluirlos, lo que debería ser trivial (por ejemplo, agregando un -e '^\s*$'en grepuno).

Respuesta2

Usando cualquier awk en cualquier shell en cada caja 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

Respuesta3

Usando perljunto con el allmétodo del List::Utilmódulo podemos detectar las líneas deseadas (todas las palabras con al menos un carácter duplicado)

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

Usando GnU sedpodemos seleccionar las líneas deseadas cuando nos aseguramos de que todos los campos deseados se extiendan desde el principio de la línea hasta el final.

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

Otra forma sedes comprobar progresivamente si hay un carácter duplicado entre los caracteres que no son espacios en blanco y no imprimir el espacio del patrón tan pronto como no se encuentren duplicados en una serie de caracteres que no sean espacios en blanco.

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

Utilizamos awk y luego recorremos cada palabra y cada carácter de una palabra. Divida la palabra sobre el carácter y verifique si se divide en más de 2 partes => se detectó dup en esta palabra. Del mismo modo, al final de la línea actual, si el recuento de duplicados detectados es igual al número de campos => línea apta para imprimir.

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

Respuesta4

Aquí hay otra solución en 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

información relacionada