使用sed将行中出现的字符范围的所有其他字符替换为该范围内行中的第一个字符



对于一个非常简单的任务来说,这是一个非常复杂的标题(至少在理论上是这样)我想写一个sed脚本,在一定条件下,用匹配的第一个数字的第一位替换所有数字。因此:

12345变成11111

我想不出我应该在替换部分写什么第一个猜测是使用像这样的回溯

s/([0-9])/1/g

这匹配了每个数字,但当然,用相同的数字替换它,因为每个匹配都是不同的,引用在规则上,而不是匹配。我如何保留第一场比赛的信息,并用它来替换行中的数字?

我认为没有像一行正则表达式替换这样简单的解决方案。

假设<>是一个没有出现在您的输入中的字符序列,您可以使用以下简短的程序

echo '1234 abc 456' | 
sed -e 's/(^[^0-9]*)([0-9])/12<>/g ; tLOOP bEND
:LOOP s/([0-9])<>([^0-9]*)[0-9]/121<>/g; tLOOP s/<>//g ; :END'

最初,在第一个数字之后插入标记CCD_ 2。如果已插入标记,则进入循环(tLOOP),其中标记后的第一个数字被替换为标记前的数字,并且标记在循环中的数字序列中移动。最后,标记被移除。

(如果上一个替换(s)匹配,则命令t跳转到标签。b无条件跳转。)

如果调试循环(在:LOOP之后使用l0;命令),您将获得以下调试输出:

1<>234 abc 456$
11<>34 abc 456$
111<>4 abc 456$
1111<> abc 456$
1111 abc 1<>56$
1111 abc 11<>6$
1111 abc 111<>$

如果您对"如何拖动关于替换遍历行的信息">更感兴趣,那么您可以稍微修改上述解决方案,使其更通用,标记将成为一种"遍历上下文":

echo '1234 abc 456' | 
sed -e 's/(^[^0-9]*)([0-9])/1<2>2/g ; tLOOP bEND
:LOOP s/<([^>]*)>([^0-9]*)[0-9]/21<1>/g; tLOOP s/<[^>]*>//g ; :END'

这一个获取信息(第一个数字),将其放入<>中,并在在行中移动时保持它。调试输出为:

<1>1234 abc 456$
1<1>234 abc 456$
11<1>34 abc 456$
111<1>4 abc 456$
1111<1> abc 456$
1111 abc 1<1>56$
1111 abc 11<1>6$
1111 abc 111<1>$

一般的想法是,您可以使用替换来操作循环中的模式空间和"遍历上下文"。遍历不限于向右移动,或通过一个位置等。(你可能可以通过这种方式实现图灵机…)

只需使用awk:

$ echo '1234 abc 456' | awk '{gsub(/[0-9]/,substr($0,1,1))} 1'
1111 abc 111

或者如果您的线路可以以非数字开头:

$ echo 'foo 1234 abc 456' | awk 'match($0,/[0-9]/){gsub(/[0-9]/,substr($0,RSTART,1))} 1'
foo 1111 abc 111

对于CCD_ 10的第三个arg使用GNU awk稍微简短一点:

$ echo 'foo 1234 abc 456' | awk 'match($0,/[0-9]/,d){gsub(/[0-9]/,d[0])} 1'
foo 1111 abc 111

如果你想让自己头疼地弄清楚gsub()中substr()中的match()的作用,你也可以写:

$ echo 'foo 1234 abc 456' | awk '{gsub(/[0-9]/,substr($0,match($0,/[0-9]/),1))} 1'
foo 1111 abc 111

这可能对你有用(GNU sed):

sed -r 's/[0-9]/n/2g;:a;s/(([0-9]).*)n/12/;ta' file

这将用换行符替换除第一位以外的所有数字(根据seds的定义,该字符不可能存在于纯行中)。然后,它使用一个循环来向后遍历文件,将每一行换行符替换为该行中唯一的数字。

如果你对GNU Awk很满意,这里有这样一个脚本:

script.awk

{ if( match( $0, /^[^0-9]*([0-9])/, inf ) ) {
$0=gensub( /[0-9]/, inf[1], "g")
}
print $0
}

这样使用:awk -f script.awk yourfile

  • 它试图将每行的第一个数字与CCD_ 12函数相匹配
  • 第一父代内部的部分匹配的实际字符串保存在CCD_ 13内部
  • 则使用CCD_ 15函数将行中的所有数字替换为CCD_
echo '1234 abc 456' | awk '{gsub(/234|456/,"111")}1'
1111 abc 111

相关内容

  • 没有找到相关文章

最新更新