Ich habe Eingaben wie diese
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
ich versuche Zeilen zu drucken, in denen jedes Wort enthältmindestens zwei gleiche Charaktere, mit grep command
Die längste Zeile enthält 8 Wörter. Ich denke, ich kann es so lösen, aber ich habe das Gefühl, dass das der falsche Weg ist.
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 ...
erwartete Ausgabe
LTCYMM SVNNDA DTVEV QLOPGO CUPUR
KKPQBP BKDKRU ZTPDPL ZRLUEL HRZZKO KXSKCU YZQTBT RISNKS
Antwort1
Mit perl
:
$ perl -ne 'print unless grep {!/(.).*\1/} /\S+/g' file
LTCYMM SVNNDA DTVEV QLOPGO CUPUR
KKPQBP BKDKRU ZTPDPL ZRLUEL HRZZKO KXSKCU YZQTBT RISNKS
Oder mit grep
Implementierungen mit Unterstützung für Perl-ähnliche reguläre Ausdrücke:
$ grep -Pve '(?<!\S)(?!\S*(\S)\S*\1)\S' file
LTCYMM SVNNDA DTVEV QLOPGO CUPUR
KKPQBP BKDKRU ZTPDPL ZRLUEL HRZZKO KXSKCU YZQTBT RISNKS
Das druckt die Zeilen, dienicht(mit -v
) enthält ein \S
(Nicht-Leerzeichen), dem kein anderes Nicht-Leerzeichen ( ) vorangeht (?<!\S)
(oder mit anderen Worten der Anfang eines durch Leerzeichen abgegrenzten Wortes ist) und das nicht der Anfang einer Folge von Nicht-Leerzeichen ist, von denen eines wiederholt wird ( (?!\S*(\S)\S*\1)
). Also im Wesentlichen ähnlich (wenn auch weniger lesbar) wie der perl
obige Ansatz.
Beachten Sie, dass sie auch leere Zeilen drucken (da sie keine Wörter enthalten, die keine wiederholten Zeichen enthalten). Wenn Sie sie nicht möchten, können Sie sie ausschließen, was trivial sein sollte (z. B. durch Hinzufügen von a -e '^\s*$'
in der grep
Eins).
Antwort2
Verwenden eines beliebigen awk in einer beliebigen Shell auf jeder Unix-Box:
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
Antwort3
Mithilfe perl
der all
Methode aus dem List::Util
Modul können wir die gewünschten Zeilen erkennen (alle Wörter mit mindestens einem doppelten Zeichen).
perl -MList::Util=all -lane '
print if all { /(.).*\1/ } @F;
' file
Mithilfe von GnU sed
können wir die gewünschten Zeilen auswählen, wenn wir sicherstellen, dass sich alle gewünschten Felder vom Zeilenanfang bis zum Zeilenende erstrecken.
$ sed -En '/^\s*(\S*(\S)\S*\2\S*(\s+|$))+$/p' file
Eine andere Möglichkeit sed
besteht darin, schrittweise nach Duplikaten unter den Zeichen ohne Leerzeichen zu suchen und den Musterbereich nicht zu drucken, sobald in einer Zeichenfolge ohne Leerzeichen keine Duplikate gefunden werden.
sed -Ee 'h
:loop
s/^\s+|\s+$//g
s/\S+/&\n/
/(\S).*\1.*\n/!d
s/^[^\n]*\n//
/./bloop
g
' file
Wir verwenden awk und durchlaufen dann jedes Wort und jedes Zeichen in einem Wort. Teilen Sie das Wort über das Zeichen auf und prüfen Sie, ob es in mehr als zwei Teile zerfällt => Duplikat in diesem Wort erkannt. Ebenso am Ende der aktuellen Zeile, wenn die Anzahl der erkannten Duplikate der Anzahl der Felder entspricht => Zeile druckbar.
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
Antwort4
Hier ist eine andere Lösung in reinem Bash – nein perl
, nein grep
, nein 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