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