私はこのように入力しました
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
または、grep
Perl のような正規表現をサポートする実装の場合:
$ 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)
) が付いていない (つまり、空白で区切られた単語の始まり) (非空白文字) が含まれており、その 1 つが繰り返される非空白文字のシーケンスの始まり ( (?!\S*(\S)\S*\1)
) ではありません。したがって、本質的には上記のアプローチに似ています (ただし、読みやすさは劣ります) perl
。
空白行も出力されることに注意してください (空白行には繰り返し文字のない単語は含まれないため)。空白行が不要な場合は、簡単に除外できます (たとえば、 1 行-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
と一緒に使用することで、必要な行(少なくとも1つの重複した文字を含むすべての単語)を検出できます。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 つ以上の部分に分割されるかどうかを確認します => この単語で重複が検出されます。同様に、現在の行の末尾で検出された重複の数がフィールドの数と等しい場合は => 行を印刷に適合させます。
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