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

각 단어에 포함된 줄을 인쇄하려고 합니다.동일한 문자가 2개 이상 있어야 합니다., 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*$'거기에 추가 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모듈 all의 메소드와 함께 사용하면 List::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개 이상으로 분할되었는지 확인합니다. => 해당 단어에서 dup이 감지됩니다. 마찬가지로, 감지된 중복 개수가 필드 수 => 인쇄에 적합한 행과 같으면 현재 줄의 끝입니다.

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

관련 정보