使用 sed 替换变量数据时保留尾随字符

问题描述 投票:0回答:4

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

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。鲍勃是鲍勃11的父亲

我最接近的是这样的

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

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

Boband Jane 要去拜访 p111 的父亲 Paul Bobis

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

谢谢。

regex bash sed
4个回答
2
投票

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

\1
,
\2
, ...,
\9
:

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

还有一种替代方法,lookaheads,在您的

sed
版本中可能可用,也可能不可用,如果可用,则需要启用其正则表达式语法的“perl 模式”。


0
投票

这对我有用:

sed s/p1\\b/Bob/g

是代表字边界的零宽度断言。


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
}

0
投票

单行调用 sed 3 次,以相反顺序替换 p111,p11,p1。

echo "grep p1 和 p11 将访问 p111。p1 是 p111 的父亲"|sed 's/p111/Paul/g'|sed 's/p11/Jane/g'|sed 's/p1/Bob /g'

© www.soinside.com 2019 - 2024. All rights reserved.