我有这样的输入
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