パターンマッチングから重複文字を除外

パターンマッチングから重複文字を除外

文字セットの文字を一度だけ一致させることができる正規表現はありますか?つまり、キャラクタが見つかるとセットから削除されます。

grepがこれを行うことができない場合、それを実行できる組み込みユーティリティはありますか?

例:

Characters to match only once:   spine

入力する:

spine
spines
spin
pine
seep 
spins

出力:

spine
spin
pine

編集する:
この出力を取得する方法はいくつかありますが(以下の例を参照)、一致させたいパターンごとにカスタムコマンドを作成せずにこれを達成する方法を探しています。

grep '[spine]' input_file | grep -v 's.*s' | ... | grep -v 'e.*e'

答え1

そして一般的な表現数学的には可能ですが、正規表現のサイズはアルファベットのサイズに比べて指数関数的に増えて実用的ではありません。

否定を使用する簡単な方法があります。逆参照

grep '[spine]' | grep -Ev '([spine]).*\1'

最初grepは1つ以上を含む行を選択しeinps、2番目はgrep2つ以上を含む行を拒否します(たとえば、andは許可しますspinal tapが、orspendは許可しません)。foobarsee

答え2

あなたの表現に触発され、egrepを使用してより短い表現を思い出すことができます。

egrep -v '(s.*s|p.*p|i.*i|n.*n|e.*e)' FILE

これは次のとおりです。

sed /s.*s/d;/p.*p/d;/i.*i/d;/n.*n/d;/e.*e/d; FILE

以下は、入力からsedコマンドを自動的に生成する方法です。

#!/bin/bash
word=$1
file=$2
expr=$(for c in $(echo $word | sed 's/./& /g'); do echo -n "/"$c".*"$c"/d;"; done);
sed $expr $file 

grepを使用して同様のアプローチを試しましたが、シェルが変数からgrepパターンを取得するように説得することはできませんでしたが、それをエコーし​​てカットして貼り付けることで結果を挿入すると、コマンドは機能します。

expr="'("$(for c in $(echo $wort | sed 's/./& /g'); do echo -n $c".*"$c"|"; done)

egrep -v ${expr/%|/)\'} FILE
# doesn't work, filters nothing, whole file is printed
# check:    
echo egrep -v $(echo $exp) FILE 
egrep -v '(s.*s|p.*p|i.*i|n.*n|e.*e)' FILE
# manually: 
egrep -v '(s.*s|p.*p|i.*i|n.*n|e.*e)' FILE
spine
spin
pine

私は間違いをしたかもしれませんし、変数の拡張に間違いをしたかもしれません。

答え3

これは、文字列が何であるかを事前に知る必要がない非正規表現アプローチです。最も効率的だとは言えませんが、私の要件には十分に高速です。

$ (echo a;echo abc;echo aabc;echo def;echo two words;echo one pair) | awk '
>   {
>     split($0,a,"");
>     n=asort(a);
>     for(i=1;i<=n;i++){
>       if(a[i]==a[i+1]){
>         next
>       }
>     }
>   }
>   n'
a
abc
def
one pair

その機能は、各行を$0配列に分割し、配列を並べ替えてa配列nの長さを返すことです。その後、配列を繰り返し、並べ替えられた配列内の2つの隣接する文字が同じ場合、次の単語で終わります。単語全体を通過すると、(すべて)入力行が印刷されます。 3つ以上の単語で構成される行は、繰り返される空白のため常に印刷されません。

はい - 繰り返し文字がない5文字の単語をすべて検索します。

$ grep '^.....$' /usr/share/dict/words | tr '[A-Z]' '[a-z]' | awk '{split($1,a,"");n=asort(a);for(i=1;i<=n;i++){if(a[i]==a[i+1]){next}}}n' | head -5
abhor
abide
abies
abilo
abler

関連情報