Drucken Sie Zeilen, in denen jedes Wort zwei gleiche Zeichen enthält, in Linux

Drucken Sie Zeilen, in denen jedes Wort zwei gleiche Zeichen enthält, in Linux

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 grepImplementierungen 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 perlobige 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 grepEins).

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 perlder allMethode aus dem List::UtilModul 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 sedkö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 sedbesteht 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

verwandte Informationen