Sed - 仅在包含大括号的行内替换

时间:2015-07-30 21:44:57

标签: sed

我整天都在苦苦挣扎。试图在一行中只包含大括号内的变量。
线条看起来像这样:

blah blah [ae b c]  blah [zv y] blah

我需要这样做:

blah blah [$ae $b $c] blah [$zv $y] blah

必须有一个简单的方法来做到这一点。但是,每当我尝试

$ echo "blah blah [ae b c]  blah [zv y] blah" | sed 's/\[\(\b.*\b\)\]/$\1/g'

我得到了贪婪的匹配,只有一个变量:

blah blah $ae b c]  blah [zv y blah

还有更好的东西吗? 谢谢,

5 个答案:

答案 0 :(得分:2)

$ echo "blah blah [ae b c]  blah [zv y] blah" | sed -r ':b; s/([[][^]$]* )([[:alnum:]]+)/\1$\2/g; t b; s/[[]([[:alnum:]])/[$\1/g'
blah blah [$ae $b $c]  blah [$zv $y] blah

如何运作

  • -r

    这会打开扩展的正则表达式。

  • :b

    这会创建一个标签b

  • s/([[][^]$]* )([[:alnum:]]+)/\1$\2/g

    这会查找[,后跟除]$之外的任何内容,后跟空格,后跟任何字母数字字符。它将$放在字母数字字符前面。

    请注意,使[[][匹配的awk约定与[^]$]匹配除]$之外的任何内容。这比尝试使用反斜杠转义这些字符更容易。

  • t b

    如果上面的命令导致替换,则将其分支回标签b,以便再次尝试替换。

  • s/[[]([[:alnum:]])/[$\1/g

    最后一步是查找[后跟字母数字字符,并在它们之间放置$

因为使用了[[:alnum:]],所以此代码是unicode-safe。

Mac OSX(BSD)版本

在BSD sed(OSX)上限制了使用分号组合语句的能力。试试这个:

sed -E -e ':b' -e 's/([[][^]$]* )([[:alnum:]]+)/\1$\2/g' -e 't b' -e 's/[[]([[:alnum:]])/[$\1/g'

答案 1 :(得分:0)

要禁用它是贪婪的,而不是匹配任何字符,请匹配除右括号之外的任何字符:

sed 's/\[\(\b[^]]*\b\)\]/$\1/g'

使用sed无法完成您要执行的任务,因为无法使用常规语法描述上下文相关匹配。

答案 2 :(得分:0)

使用很难解决它。作为替代方案,您可以在Text::Balanced模块的帮助下使用,它可以在平衡分隔符(如方括号)之间提取文本。每次调用都返回一个数组,其中包含分隔符之间的内容,它们之前的文本和它们之后的文本,因此您可以将$符号的正则表达式应用于字符串的有意义部分。

perl -MText::Balanced=extract_bracketed -lne '
    BEGIN { $result = q||; }
    do {
        @result = extract_bracketed($_, q{[]}, q{[^[]*});
        if (! defined $result[0]) {
            $result .= $result[1];
            last;
        }
        $result[0] =~ s/(\[|\s+)/$1\$/g;
        $result .= $result[2] . $result[0];
        $_ = $result[1];
    } while (1);
    END { printf qq|%s\n|, $result; }

' infile

它产生:

blah blah [$ae $b $c]  blah [$zv $y] blah

答案 3 :(得分:0)

sed 's/\[\([^]]*\)\]/[ \1]/g
:loop
s/\(\(\[[^]$]*\)\([[:blank:]]\)\)\([^][:blank:]$][^]]*\]\)/\1\$\4/g
t loop
s/\[ \([^]]*\)\]/[\1]/g' YourFile
  • posix version
  • 假设[a b[c] d ]
  • 中的括号内没有括号
  • ALGO:
    • 在打开括号后添加空格字符(需要使用空白作为起始字分隔符,通常没有第一个空格)
    • 循环标签锚
    • 在支架之间的最后一个单词前面添加一个$,但没有一个(不是以$开头)。对每个支架组执行此操作,但仅为每个组添加1个
    • 如果发生,请再次尝试标记循环
    • 删除第一个操作中添加的第一个空格

答案 4 :(得分:0)

这可能适合你(GNU sed):

sed -r 'h;s/\</$/g;T;G;s/^/\n/;:a;s/\n[^[]*(\[[^]]*\])(.*\n)([^[]*)[^]]*\]/\3\1\n\2/;ta;s/\n(.*)\n(.*)/\2/' file

制作当前行的副本。在所有词头界限的前面插入$。如果没有替代品,则打印当前行并打包。否则,附加未掺杂行的副本,并在掺假当前行的开头插入换行符。使用替换和模式匹配使用换行符替换[...]与原始匹配部分之间的行部分,以便将匹配向前移动到该行。完成所有匹配后,替换原始行的末尾并删除换行符。