是否有一个正则表达式可以匹配字符集中的字符但只匹配一次?换句话说,一旦找到一个字符,就将其从集合中删除。
如果 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 的类似方法,但无法说服 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
也许我犯了一个错误,也许我在变量扩展方面犯了错误。
答案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
数组的长度。然后,它遍历数组,如果排序数组中的两个相邻字符相同,则退出到下一个单词。如果它一直通过单词,它将打印(整个)输入行。请注意,由于空格重复,三个单词或更多单词的行始终无法打印。
示例 - 查找没有重复字符的所有五个字母单词:
$ 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