sed / awk或其他:one-liner将数字增加1,保持间距字符

时间:2010-04-21 22:28:58

标签: unix shell sed awk

编辑:我事先并不知道我的数字将在哪个“栏目”,我希望有一个单行。显然sed不做算术,所以也许基于awk的单行解决方案?

我有一个字符串:(注意间距)

eh oh    37

我希望它成为:

eh oh    36

(所以我想保留间距)

使用 awk 我找不到怎么做,到目前为止我有:

echo "eh oh   37" | awk '$3>=0&&$3<=99 {$3--} {print}'

但是这给了:

eh oh 36

(丢失的间距字符,因为字段分隔符是'')

有没有办法让 awk 那样使用与输入完全相同的字段分隔符来打印输出“

然后我尝试了其他东西,使用awk的 sub(..,..)方法:

' sub(/[0-9][0-9]/, ...) {print}'

但还没有雪茄:我不知道如何引用正则表达式并在第二个参数中对其进行算术运算(我现在用'...'留下)。

然后我尝试用sed,但在此之后卡住了:

echo "eh oh   37" | sed -e 's/\([0-9][0-9]\)/.../' 

我可以使用对匹配数字的引用从 sed 进行算术运算,并且输出不会修改间距字符数吗?

请注意,这与我关于Emacs的问题以及如何将其应用于某些(大型)Emacs区域(使用Emacs的 shell-command-on-region 的替换区域)有关,但它不是相同的问题:这个问题具体是关于在使用awk / sed / etc时如何“保留空间”。

5 个答案:

答案 0 :(得分:5)

这是 ghostdog74的答案的变体,不需要将数字锚定在字符串的末尾。这是使用match而不是依赖于特定位置的数字来完成的。

这将替换第一个数字,其值减去1:

$ echo "eh oh    37      aaa     22    bb" | awk '{n = substr($0, match($0, /[0-9]+/), RLENGTH) - 1; sub(/[0-9]+/, n); print }'
eh oh    36      aaa     22    bb

使用gsub而不是sub会将“37”和“22”替换为“36”。如果线路上只有一个号码,那么您使用哪个号码无关紧要。但是,通过这种方式,它将处理带有尾随空格的数字以及可能存在的其他非数字字符(在某些空格之后)。

如果你有gawk,可以像这样使用gensub来挑选字符串中的任意数字(只需设置which的值):

$ echo "eh oh    37      aaa     22    bb    19" |
    awk -v which=2 'BEGIN { regex = "([0-9]+)\\>[^0-9]*";
        for (i = 1; i < which; i++) {regex = regex"([0-9]+)\\>[^0-9]*"}}
        { match($0, regex, a);
        n = a[which] - 1;                    # do the math
        print gensub(/[0-9]+/, n, which) }'
eh oh    37      aaa     21    bb    19

第二个(which=2)数字从22变为21.并且嵌入的空格被保留。

它在多行上分解,以便于阅读,但它是复制/可管理的。

答案 1 :(得分:3)

$ echo "eh oh    37" | awk '{n=$NF+1; gsub(/[0-9]+$/,n) }1'
eh oh    38

$ echo "eh oh    37" | awk '{n=$NF+1; gsub(/..$/,n) }1'
eh oh    38

答案 2 :(得分:2)

类似

number=`echo "eh oh 37" | grep -o '[0-9]*'`
sed 's/$number/`expr $number + 1`/'

答案 3 :(得分:1)

怎么样:

$ echo "eh oh   37" | awk -F'[ \t]' '{$NF = $NF - 1;} 1'
eh oh   36

答案 4 :(得分:0)

解决方案不会保留小数位数,所以如果数字为10,则结果为9,即使有人希望得到09。

我没有编写尽可能短的代码,它应该保持可读

这里我使用RLENGTH构造printf模式,因此它变为%02d(2是匹配模式的长度)

$ echo "eh oh    10      aaa     22    bb" |
    awk '{n = substr($0, match($0, /[0-9]+/), RLENGTH)-1 ; 
          nn=sprintf("%0" RLENGTH "d", n)
          sub(/[0-9]+/, nn);
          print 
         }'
eh oh    09      aaa     22    bb