对于一个非常简单的任务来说,这是一个非常复杂的标题(至少在理论上是这样)我想写一个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