需要的诡计有两个部分:
当您到达一个闭合的方括号时停止替换(但在行上重复执行):
s/\(\[[^] ]*\) /\1_/g
这匹配一个左方括号,后跟零个或多个既不是空白也不是右方括号的字符。全局后缀意味着该模式适用于所有以开方括号开头的序列,最后是行上的空白或右方括号。还要注意,这个正则表达式不会改变 ' [single-word] and context
',而原始表达式会将其转换为 ' [single-word]_and context
',这不是练习的对象。
获取 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]