0

如果我有一个像这样的字符串:

p1 and p11 are going to visit p111. p1 is the father of p111

我怎么能使用 sed (或任何东西,真的)用不同的值替换 p{n} 的每个实例?所以结果会是这样的:

Bob and Jane are going to visit Paul. Bob is the father of Paul

基本上,我正在寻找一种方法来告诉 sed,“准确地找到 p{n} 后跟除数字以外的任何内容,并将其替换为 $var,但不要替换 {n} 后面的内容。”

如果我做一些简单的事情

text="p1 and p11 are going to visit p111. p1 is the father of p111"
text=`echo "$text" | sed s/p1/Bob/g`

我最终将每次出现的“p1”替换为“Bob”,并且不会发生后续替换:

Bob 和 Bob1 将访问 Bob11。Bob 是 Bob11 的父亲

我最接近的是

text=`echo "$text" | sed 's/p1[^0-9]/bob/g'`

这有两个问题:它使用尾随字符(空格、标点符号),并且它不匹配行尾的 p{n}。遍历所有需要替换的内容后:

Boband Janeare 去拜访 p111 的父亲 Paul Bobis

任何人都知道如何找到我需要替换的内容,而不是插入其他变量,并且不使用尾随的非数字字符?

谢谢。

4

3 回答 3

2

当然。诀窍是使用匹配组保留您不想丢失的任何内容,由转义括号分隔,并使用反向引用\1, \2, ...,将其带入替换字符串\9

s/p1\([^0-9]\)/Bob\1/g

还有一种替代方法,lookaheads,它可能在您的版本中可用,也可能不可用sed,如果可用,则需要启用其正则表达式语法的“perl 模式”。

于 2012-06-25T10:21:48.403 回答
0

您可以构建一个包含所需替换的简单文件,将其命名为data

1 Bob
11 Jane
111 Paul

然后使用 awk 阅读:

awk 'BEGIN{ while( getline d < "data" ) { split(d,a); r[a[1]]=a[2]}}
  { for( i in r ) gsub( "p"i, r[i])}1' input

请注意,这可能会也可能不会按原样工作,具体取决于数组的构建方式。在我的实现中,r 的迭代有效,因为返回的顺序恰好是“111”、“11”、“1”,但这肯定不是明确定义的行为。您可以通过每次读取数据文件而不是将其读入数组来强制替换所需的顺序:

awk '{
  while( getline d < "data" ) { 
    split( d,a ); 
    gsub( "p"a[1],a[2])
  }
  close("data")}1' input

这要求您在构建查找文件时要小心,并且在这种情况下要求数据行与上面给出的相反。如果您更喜欢添加单词分隔符,使用 perl 可能更容易:

use autodie;
open my $f, "<", "data";
while(<$f>) {@a = split; $n{$a[0]} = $a[1]}
while(<>) {
  foreach $i (keys %n ) { s/p$i(\W)/$n{$i}$1/g }
  print
}
于 2012-06-25T14:27:13.560 回答
0

这对我有用:

sed s/p1\\b/Bob/g

\b 是代表单词边界的零宽度断言。

于 2012-06-25T18:46:15.283 回答