Linux에서 각 단어에 두 개의 동일한 문자가 포함된 줄을 인쇄합니다.

Linux에서 각 단어에 두 개의 동일한 문자가 포함된 줄을 인쇄합니다.

이런 입력이 있습니다

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

각 단어에 다음이 포함된 줄을 인쇄하려고 합니다.적어도 두 개의 동일한 문자, grep 사용 command 가장 긴 줄에는 8개의 단어가 포함되어 있습니다. 그렇게 하면 해결할 수 있을 것 같지만 방법이 잘못된 것 같습니다.

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

예상 출력

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

답변1

와 함께 perl:

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

또는 grepPerl과 같은 정규식을 지원하는 구현을 사용하면 다음과 같습니다.

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

그러면 다음과 같은 줄이 인쇄됩니다.~ 아니다( -v) 에는 \S공백이 아닌 다른 문자( (?<!\S))(또는 공백으로 구분된 단어의 시작인 IOW)가 앞에 있지 않고 공백이 아닌 일련의 시작이 아닌 (공백이 아닌 문자)가 포함되어 있습니다. 그 중 ( (?!\S*(\S)\S*\1))이 반복됩니다. 따라서 본질적으로 perl위의 접근 방식과 유사합니다(가독성은 떨어지지만).

또한 빈 줄도 인쇄한다는 점에 유의하세요(반복되는 문자가 없는 단어는 포함되지 않기 때문). 원하지 않는 경우에는 사소한 항목을 제외할 수 있습니다(예: 항목 -e '^\s*$'에 a를 추가하는 방법 grep).

답변2

모든 Unix 상자의 모든 쉘에서 awk를 사용하십시오.

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

답변3

모듈 의 방법 perl과 함께 사용하면 원하는 줄을 감지할 수 있습니다(중복된 문자가 하나 이상 있는 모든 단어).allList::Util

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

를 사용하면 GnU sed원하는 모든 필드가 줄의 시작부터 끝까지 늘어나도록 보장할 때 원하는 줄을 선택할 수 있습니다.

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

또 다른 방법은 sed공백이 아닌 문자 사이에서 중복된 문자를 점진적으로 확인하고 공백이 아닌 문자에서 중복이 발견되지 않는 즉시 패턴 공간을 인쇄하지 않는 것입니다.

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

우리는 awk를 활용한 다음 모든 단어와 단어의 모든 문자를 반복합니다. 문자로 단어를 분할하고 2개 이상의 부분으로 나뉘는지 확인합니다. => 이 단어에서 중복이 감지되었습니다. 감지된 중복 횟수가 필드 수 => 인쇄에 적합한 줄인 경우 현재 줄 끝에서도 마찬가지입니다.

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

답변4

순수 Bash의 또 다른 솔루션은 다음과 같습니다. 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

관련 정보