title=
仅当空格出现在文本文件中字符串之后和之间时,如何使用 sed 用下划线替换空格" "
例如(文本文件中的行):
title="This is the title of my book" img=scr " </header><!-- .entry-header -->
title="Today is a beautiful day" img=scr " </header><!-- .entrrrrkkky-header -->
sed 之后所需的修改文本文件:
title="This_is_the_title_of_my_book" img=scr " </header><!-- .entry-header -->
title="Today_is_a_beautiful_day" img=scr " </header><!-- .entrrrrkkky-header -->
基本上,只有当空格出现在字符串后面时,才会替换 a
_
" "
title=
文本文件的名称是任意的 - 比如file.txt
答案1
你必须循环执行此操作:
s/\(^.*title="[^" ]*\) \([^"]*".*$\)/\1_\2/
或(更快)
s/\(title="[^" ]*\) \([^"]*"\)/\1_\2/
或者(可能又更快,因为为什么要添加另一个匹配项来替换它本身?!)
s/\(title="[^" ]*\) /\1_/
并使用 sed 的测试和分支功能,重试替代项,直到不再进行任何更改。此命令中模式的要点是在第一个(剩余)空格处分割行,并用下划线替换该空格。
这是一个脚本:
#!/bin/sh
sed -e ':loop' \
-e 's/\(title="[^" ]*\) \([^"]*"\)/\1_\2/' \
-e 't loop' <foo.in >foo.out
diff -u foo.in foo.out
最初的答案使用了更广泛的模式,但@g-man 评论说这是没有必要的。它速度较慢,如在 10Mb 文件上对 sed 进行计时所示(在 Debian 7 上使用 GNU sed 进行测试):
$ ./foo1
27.03user 0.01system 0:27.18elapsed 99%CPU (0avgtext+0avgdata 1104maxresident)k
0inputs+0outputs (0major+333minor)pagefaults 0swaps
9.54user 0.00system 0:09.60elapsed 99%CPU (0avgtext+0avgdata 972maxresident)k
0inputs+0outputs (0major+301minor)pagefaults 0swaps
对于 OSX,差异没有那么大:
$ ./foo1
real 0m11.943s
user 0m11.897s
sys 0m0.024s
real 0m5.858s
user 0m5.839s
sys 0m0.014s
有趣的是,较宽的模式不适用于 Solaris 的 sed(但较短的模式可以)。它与分组内的线的任一端都不匹配\(
,并且\)
中的行的任何一端,而 BSD 和 GNU 都这样做。同样,它适用于 HPUX 11.31 和 AIX 7.1
POSIX sed 使用 BRE,分组功能由9.3.6 匹配多个字符的 BRE:
"\("
可以通过将子表达式括在字符对和之间来在 BRE 中定义子表达式"\)"
。这样的子表达式应匹配没有“(”和“)”时它会匹配的任何内容,除非子表达式中的锚定是可选行为;看BRE 表达锚定。子表达式可以任意嵌套。
9.3.8 BRE 表达锚定解释该术语:
BRE 可以限制为匹配以行开头或结尾的字符串;这称为“锚定”。
因此,在标准实施的上下文中,这是 Solaris 的一个已知限制,sed
标准允许将其作为“可选”行为。
进一步阅读:
- 3.7 sed 大师的命令(描述标签和循环)