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 grep
implementaciones 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 perl
enfoque 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 grep
uno).
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 perl
junto con el all
método del List::Util
mó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 sed
podemos 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 sed
es 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