在 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

我正在嘗試打印每個單字包含的行至少有兩個相同的字符,使用 grepcommand 最長的一行包含 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

或使用grep支援類似 perl 正規表示式的實作:

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

列印出來的行不是(with -v) 包含一個\S(非空白字元),該字元前面沒有另一個非空白 ( (?<!\S)) (或 IOW 是空白分隔單字的開頭),並且不是一系列非空白字元的開頭其中重複的是( (?!\S*(\S)\S*\1))。因此本質上類似於(儘管不太清晰)perl上述方法。

請注意,它們也會列印空白行(因為它們不包含沒有重複字元的單字)。如果您不需要它們,您可以排除它們,這應該是微不足道的(例如透過-e '^\s*$'在其中添加 a grep)。

答案2

在每個 Unix 機器上的任何 shell 中使用任何 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 中的另一個解決方案 - 不perl,不grep,不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

相關內容