2

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

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

即,为任何以文字“[”开头的内容创建一个反向引用捕获,该文字包含任意数量的非空格字符,后跟一个空格,以替换为非空格字符,后跟一个下划线。我还不知道如何改变这个表达式,使其适用于大括号内的所有下划线,例如 [a few words] 到 [a_few_words]。

我感觉我已经很接近了,但我只是错过了一大块知识,这些知识将在一行(SQL Server 的在这种情况下为 DDL)。

任何建议都非常感谢......

4

2 回答 2

3

需要的诡计有两个部分:

  1. 当您到达一个闭合的方括号时停止替换(但在行上重复执行):

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

    这匹配一个左方括号,后跟零个或多个既不是空白也不是右方括号的字符。全局后缀意味着该模式适用于所有以开方括号开头的序列,最后是行上的空白或右方括号。还要注意,这个正则表达式不会改变 ' [single-word] and context',而原始表达式会将其转换为 ' [single-word]_and context',这不是练习的对象。

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

其中两个鲜为人知的操作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' 限定符消失了 - 考虑到循环,其他变体中可能不需要它;它的存在可能会使过程稍微更有效率,但很可能基本上不可能检测到这一点。模式现在是锚定到行首(插入符号),并且在第一个左方括号之前包含零个或多个不是左方括号的字符。)

样本输出:

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]
于 2010-12-21T21:35:28.137 回答
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替换标志会导致它被评估,因此您可以调用一个函数来完成内部工作。

于 2010-12-21T21:48:55.363 回答