使用sed

时间:2016-06-04 15:52:53

标签: bash unix sed

一个非常简单的任务非常复杂的标题(至少是teorically) 我希望编写一个sed脚本,在一定条件下,用匹配的第一个数字的第一个数字替换所有数字。那样:

12345成为11111

我无法弄清楚我应该在替换部分写些什么 第一个猜测是使用像这样的反射

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

这匹配每个数字但当然用相同的数字替换它,因为每个匹配都是不同的,并且引用在规则上而不是匹配。我如何保留有关第一场比赛的信息,以及我们在线上更换数字?

5 个答案:

答案 0 :(得分:4)

我认为没有简单的解决方案,如单行正则表达式替换。

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

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

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

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

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

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]/\2\1<\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>$

一般的想法是你可以使用替换来操纵循环中的模式空间和“遍历conext”。遍历不仅限于向右移动,也不限于一个位置等(例如,你可以用这种方式实现图灵机..​​....)

答案 1 :(得分:2)

只需使用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

使用GNU awk进行第3次arg到match()稍微简短:

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

如果你想让自己弄清楚gsub()中substr()内的匹配()是什么,你也可以写:

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

答案 2 :(得分:2)

这可能适合你(GNU sed):

ERROR: Target of URI does not exist: 'package:rails_ujs/rails_ujs.dart' ([dart_app] dart_app.dart:1)

使用换行符替换除第一个数字之外的所有数字(由seds定义的字符不能存在于纯粹的行中)。然后它使用一个循环来向后处理文件,用行中找到的唯一数字替换每个换行符。

答案 3 :(得分:1)

如果您对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

  • 它尝试将每行的第一个数字与match函数匹配,
  • 第一次进行中部分匹配的实际字符串保存在inf[1]
  • 然后使用inf[1]函数
  • 将行中的所有数字替换为gensub中的数字

答案 4 :(得分:0)

echo '1234 abc 456' | awk '{gsub(/234|456/,"111")}1'
1111 abc 111