文字セット内の文字に一致するが1回だけである次の正規表現はありますか?つまり、文字が見つかったら、その文字をセットから削除します。
Grepがこれを実行できない場合、実行できる組み込みユーティリティはありますか?
例:
Characters to match only once: spine
入力:
spine
spines
spin
pine
seep
spins
出力:
spine
spin
pine
編集:
この出力を実現する方法はたくさんありますが(以下の1つの例)、一致させたいパターンごとにコマンドをカスタマイズすることなく、これを実現する方法を探しています。
grep '[spine]' input_file | grep -v 's.*s' | ... | grep -v 'e.*e'
正規表現 数学的な意味では可能ですが、正規表現のサイズはアルファベットのサイズに比べて指数関数的に大きくなるため、実用的ではありません。
否定と backreferences を使用する簡単な方法があります。
grep '[spine]' | grep -Ev '([spine]).*\1'
最初のgrep
は、einps
の少なくとも1つを含む行を選択します。 2番目のgrep
は、複数の行を含む行を拒否します(たとえば、spinal tap
およびspend
ですが、foobar
またはsee
ではありません)。
あなたの表現に触発されて、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-commandを自動的に生成する方法です。
#!/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-patternを取得するようにシェルを説得できませんでしたが、エコーアウトして結果をカットアンドペーストで挿入すると、コマンドは機能しました。
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
たぶん私は間違いを犯したのかもしれませんし、変数の展開を間違えたのかもしれません。