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 grep
implementaçõ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) à perl
abordagem 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 perl
junto com o all
método do List::Util
mó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 sed
podemos 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