Grep

模式匹配排除重複字元

  • January 6, 2022

以下是否有一個正則表達式匹配字元集中的字元但只匹配一次?換句話說,一旦找到一個角色,就將其從集合中移除。

如果 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'

使用數學意義上的正則表達式是可能的,但是正則表達式的大小相對於字母表的大小呈指數增長,因此不實用。

否定和反向引用有一個簡單的方法。

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

第一個grep選擇包含至少一個的行einps;第二個grep拒絕包含多個任何一個的行(例如,允許spinal tapspend但不允許foobarsee)。

受你的表達的啟發,我可以用 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 嘗試了類似的方法,但無法說服 shell 從變數中獲取 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

也許我犯了一個錯誤,也許我在變數擴展方面犯了一個錯誤。

引用自:https://unix.stackexchange.com/questions/17175