如何使用sed用下划线替换方括号内的所有空格?

时间:2010-12-21 20:21:59

标签: regex sed backreference

我发现为了将[some name]变成[some_name],我需要使用以下表达式:

s/\(\[[^ ]*\) /\1_/

即。为包含任意数量的非空格字符,后跟空格的文字“[”开头的任何内容创建反向引用捕获,用非空格字符后跟下划线替换。我还不知道的是如何改变这个表达式,因此它适用于大括号内的所有下划线,例如[几句]加入[a_few_words]。

我觉得我很接近,但我只是错过了一大堆知识,这将解开使得这个东西在一行中包含的第一组[] s的约束内无限次地工作的关键(在这种情况下,SQL Server DDL。)

感激地收到任何建议......

2 个答案:

答案 0 :(得分:3)

所需的诀窍有两个部分:

  1. 当你到达一个接近方括号时停止更换(但在线上重复这样做):

    s/\(\[[^] ]*\) /\1_/g
    

    这匹配一个空方括号,后跟零个或多个字符,既不是空白也不是近方括号。全局后缀表示该模式应用于以开放方括号开头的所有序列,最后是行上的空白或紧密方括号。另请注意,此正则表达式不会改变“[single-word] and context”,而原始版本会将其转换为“[single-word]_and context”,这不是练习的对象。

  2. 从这个开始的地方重复搜索。不幸的是,没有一种真正好的方法可以做到这一点。 Sed总是继续搜索被替换的文本;这是我们不想要的一个场合。有时,您只需重复替换操作就可以逃脱。在这种情况下,您必须在每次替换成功时重复此操作,在没有更多替换时停止。

  3. sed中不太知名的两项操作是“:label”和“t”命令。然而,它们出现在Unix的第7版(大约1978年)中,因此它们不是新功能。第一个只是在脚本中标识一个位置,该位置可以通过“b”(此处不需要)或“t”跳转到:

    [2addr]t [label]
    
         

    如果自最近读取输入行或执行“:”函数以来已进行任何替换,则转移到带有标签的“t”函数。如果未指定标签,则转移到脚本的末尾。

    奇妙:我们需要:

     sed -e ':redo; s/\(\[[^] ]*\) /\1_/g; t redo' data.file
    

    之外 - 它不能像这样在一行上运行(至少在MacOS X上不行)。不过,这确实令人钦佩:

    sed -e ':redo
            s/\(\[[^] ]*\) /\1_/g
            t redo' data.file
    

    或者,如评论中所述,您可以编写三个单独的'-e'选项(适用于MacOS X):

     sed -e ':redo' -e 's/\(\[[^] ]*\) /\1_/g' -e 't redo' data.file
    

    给定数据文件:

    a line with [one blank] word inside square brackets.
    a line with [two blank] or [three blank] words inside square brackets.
    a line with [no-blank] word inside square brackets.
    a line with [multiple words in a single bracket] inside square brackets.
    a line with [multiple words in a single bracket] [several times on one line]
    

    显示的sed脚本的输出是:

    a line with [one_blank] word inside square brackets.
    a line with [two_blank] or [three_blank] words inside square brackets.
    a line with [no-blank] word inside square brackets.
    a line with [multiple_words_in_a_single_bracket] inside square brackets.
    a line with [multiple_words_in_a_single_bracket] [several_times_on_one_line]
    

    最后,阅读问题中的细则,如果你只需要在每一行的第一个方括号字段中完成,那么我们需要确保在开始匹配之前没有方括号。这种变体有效:

    sed -e ':redo' -e 's/^\([^]]*\[[^] ]*\) /\1_/' -e 't redo' data.file
    

    ('g'限定符已经消失 - 在给定循环的情况下,其他变体可能不需要它;它的存在可能会使该过程略微提高效率,但很可能基本上无法检测到它。 pattern现在锚定到行的开头(插入符号),并包含零个或多个字符,这些字符在第一个方括号之前不是方括号。)

    示例输出:

    a line with [two_blank] or [three blank] words inside square brackets.
    a line with [no-blank] word inside square brackets.
    a line with [multiple_words_in_a_single_bracket] inside square brackets.
    a line with [multiple_words_in_a_single_bracket] [several times on one line]
    

答案 1 :(得分:1)

对于像perl这样具有“可执行”替换的语言来说,这更容易:

perl -wne 's/(\[.*?])/ do { my $x = $1; $x =~ y, ,_,; $x } /ge; print'

或者更清楚地分开它:

sub replace_with_underscores {
    my $s = shift;
    $s =~ y/ /_/;
    $s
}
s/(\[.*?])/ replace_with_underscores($1) /ge;

.*?是非贪婪的匹配(为了避免将两个相邻的括号中的短语混合在一起)并且替换的e标志导致它被评估,所以你可以调用一个函数来做内心的工作。

相关问题