Shell:提取匹配模式的单词,但忽略规避表达式

时间:2013-06-24 19:38:47

标签: shell nested pattern-matching ignore

我目前正在尝试从文本中提取所有匹配的表达式,例如看起来像这样,把它们放到一个数组中。

aaaaaaaaa${bbbbbbb}ccccccc${dddd}eeeee
ssssssssssssssssss${TTTTTT}efhsekfh ej
348653jlk3jß1094utß43t59ßgöelfl,-s-fko

匹配表达式与此类似:${}。要注意我需要完整的表达,而不仅仅是这个表达式之间的单词!所以在这种情况下,结果应该是一个包含以下内容的数组:

${bbbbbbb}
${dddd}
${TTTTTTT}

我偶然发现并无法解决的问题:

  1. 它不应该将此视为一个整体 ${bbbbbbb}ccccccc${dddd}但每个都是自己的
  2. grep -o未安装在旧机器上,也不允许使用Perl!
  3. 许多命令,例如BASH_REMATCH只传递整行或表达式的第一个匹配项,而不是行中所有匹配的表达式!
  4. 所提到的模式\${[^}]*}似乎部分起作用,因为它可以提取表达式的第一次出现,但是如果它在同一文本行中,它总是省略后面的那些。我需要的是在行中找到的所有匹配表达式,而不仅仅是第一个。

2 个答案:

答案 0 :(得分:1)

您可以在任何字符 $ {} 上拆分字符串:

$ s='...blaaaaa${blabla}bloooo${bla}bluuuuu...'
$ echo "$s"
...blaaaaa${blabla}bloooo${bla}bluuuuu...
$ IFS='${}' read -ra words <<< "$s"
$ for ((i=0; i<${#words[@]}; i++)); do printf "%d  %s\n" $i "${words[i]}"; done
0  ...blaaaaa
1  
2  blabla
3  bloooo
4  
5  bla
6  bluuuuu...

因此,如果您正在尝试提取大括号内的单词:

$ for ((i=2; i<${#words[@]}; i+=3)); do printf "%d  %s\n" $i "${words[i]}"; done
2  blabla
5  bla

如果以上情况不适合你,grep会工作:

$ echo '...blaaaaa${blabla}bloooo${bla}bluuuuu...' | grep -o '\${[^}]\+}'
${blabla}
${bla}

你还没有告诉我们你想要的输出。

答案 1 :(得分:0)

由于它给我带来了很多麻烦,我直接在www.unix.com上提出要求,并提供适合我古老外壳的解决方案。所以,如果有人遇到同样的问题,那就是解决方案:

line='aaaa$aa{yyy}aaa${important}xxxxxxxx${important2}oo{o$}oo$oo${importantstring3}'
IFS=\$ read -a words <<< "$line" 
regex='^(\{[^}]+})'
for e in "${words[@]}"; do
    if [[ $e =~ $regex ]]; then 
        echo "\$${BASH_REMATCH[0]}";
    fi;
done

打印出以下内容 - 甚至不会被语法正确表达式之间随机出现的${}打扰:

${important}
${important2}
${importantstring3}

我从论坛获得另一个更新后更新了完整的解决方案:现在它也忽略了这个:aaa$aa{yyy}aaaa - 之前打印为$ {yyy} - 但它应该完全忽略,因为有字符在${之间。现在,在正则表达式的开头附加锚定它可以正常工作。

我刚刚发现另一个问题:理论上使用上述方法如果读取行看起来像line='{ccc}aaaa${important}aaa',我仍会得到错误的输出。 IFS将拆分它,REGEX将匹配{ccc},尽管前面没有$符号。这不是最理想的 然而,下面的方法可以解决它:在获得BASH_REMATCH之后,我需要在原始行中进行搜索 - 我给IFS的那个 - 对于这个精确表达式${ccc} - 区别于{{1 }} 已经包括了!并且只有当它找到这种完全匹配时,才会将其视为有效匹配;否则应该被忽略。一种反向搜索方法......

更新 - 添加此反向搜索以忽略行开头的陷阱:

$

可忽略的问题:如果该行看起来像pattern="\$${BASH_REMATCH[0]}"; searchresult=""; searchresult=`echo "$line" | grep "$pattern"`; if [ "$searchresult" != "" ]; then echo "It was found!"; fi; ,它会将第一个 line='{ccc}aaaaaa${ccc}bbbbb'识别为有效匹配(尽管不是)并打印出来,因为反向搜索找到了第二个 {ccc}。虽然这并不意味着它与我的具体目的无关,因为它暗示这种模式实际上至少在同一行中存在一次。

相关问题