Bash复制每一行,再打印3行,改变结束

时间:2017-11-10 17:35:13

标签: bash shell awk sed

输入文件包含多行,如

   0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
   0     1     0     1     0     0     0     0    -1    3 / 4    1 / 4    1 / 2

我想复制输入中的每一行,在原始行下面插入3个副本,并在结尾处修改分数。我希望输出是

   0     1     0     0     0     1     1     0     0    0 / 1    0 / 1    0 / 1
   0     1     0     0     0     1     1     0     0    0 / 1    1 / 2    1 / 2
   0     1     0     0     0     1     1     0     0    1 / 2    0 / 1    1 / 2
   0     1     0     0     0     1     1     0     0    1 / 2    1 / 2    0 / 1
   0     1     0     1     0     0     0     0    -1    3 / 4    1 / 4    1 / 2
   0     1     0     1     0     0     0     0    -1    3 / 4    3 / 4    0 / 1
   0     1     0     1     0     0     0     0    -1    1 / 4    1 / 4    0 / 1
   0     1     0     1     0     0     0     0    -1    1 / 4    3 / 4    1 / 2

对分数的修改遵循模式

(0,0,0)  <- original fractions
(0,+1/2,+1/2)
(+1/2,0,+1/2)
(+1/2,+1/2,0)

但是,如果分数大于1

i.e. 3/4 + 1/2 = 5/4

它必须从中扣除1

so 5/4 -> 1/4

想将此解决方案添加到我拥有的当前bash脚本中。我作为我的&#34;输入&#34;是迄今为止我的脚本的结果。也许是一个awk或sed命令来实现预期的结果?

3 个答案:

答案 0 :(得分:1)

awk救援!

$ awk 'BEGIN{FS=OFS="\t"} 
       function addHalf(v) {split(v,a," / ");                 # split num/denom
                            n=2*a[1]+a[2]; d=2*a[2];          # add 1/2
                            if(n>=d) n-=d;                    # modulus 1
                            while(!(n%2 || d%2)) {n/=2;d/=2}  # normalize if both even
                            return n " / " d}

     {print;
      for(i=2;i>=0;i--)                   # iterate over last three fields
        {j=NF-(i+1)%3;   k=NF-(i+2)%3;    # compute indices
         tj=$j;          tk=$k;           # save values
         $j=addHalf(tj); $k=addHalf(tk);  # modify selected indices
         print;                           # print modified line
         $j=tj;          $k=tk}}' file    # revert to saved values

答案 1 :(得分:1)

这将使您获得使用GNU awk为真正的多维数组,gensub()和\ s / \ S简写来识别原始分数和应用于每个分数的增量:

$ cat tst.awk
BEGIN {
    split("\
            (0,0,0)             \
            (0,+1/2,+1/2)       \
            (+1/2,0,+1/2)       \
            (+1/2,+1/2,0)       \
        ",modRows,/[[:space:])(]+/)

    for (i=1; i in modRows; i++) {
        row = modRows[i]
        if ( row ~ /\S/ ) {
            deltas[++numRows][1]
            numCols = split(row,deltas[numRows],/,/)
        }
    }
}

{
    head = gensub(/^((\s*\S+){9})(.*)/,"\\1",1)
    tail = gensub(/^(\s*(\S+\s+){9})(.*)/,"\\3",1)
    tail = gensub(/ ([^0-9]) /,"\\1","g",tail)

    split(tail,fracts)

    for (rowNr=1; rowNr <= numRows; rowNr++) {
        printf "%s", head
        for (colNr=1; colNr <= numCols; colNr++) {
            fract = fracts[colNr]
            delta = deltas[rowNr][colNr]
            printf "%s%s", OFS, addDelta(fract,delta)
        }
        print ""
    }
}

function addDelta(oldFract,delta,       newFract) {
    newFract = "(" oldFract " + " delta ")"    # <-- do the math here!
    return newFract
}

$ gawk -f tst.awk file
   0     1     0     0     0     1     1     0     0    (0/1 + 0)       (0/1 + 0)       (0/1 + 0)
   0     1     0     0     0     1     1     0     0    (0/1 + 0)       (0/1 + +1/2)    (0/1 + +1/2)
   0     1     0     0     0     1     1     0     0    (0/1 + +1/2)    (0/1 + 0)       (0/1 + +1/2)
   0     1     0     0     0     1     1     0     0    (0/1 + +1/2)    (0/1 + +1/2)    (0/1 + 0)
   0     1     0     1     0     0     0     0    -1    (3/4 + 0)       (1/4 + 0)       (1/2 + 0)
   0     1     0     1     0     0     0     0    -1    (3/4 + 0)       (1/4 + +1/2)    (1/2 + +1/2)
   0     1     0     1     0     0     0     0    -1    (3/4 + +1/2)    (1/4 + 0)       (1/2 + +1/2)
   0     1     0     1     0     0     0     0    -1    (3/4 + +1/2)    (1/4 + +1/2)    (1/2 + 0)

所以你需要做的就是在脚本底部的addDeltas()函数中添加你想要计算的每个分数的数学。

答案 2 :(得分:0)

以下Perl脚本按照您要求的方式处理分数。它使用Number::Fraction来进行数学运算。

#!/usr/bin/perl
use strict;
use warnings;

use Number::Fraction;

while (<>) {
    print;
    my @cols = split /(\s+)/;
    for my $modify ([0, 1, 1], [1, 0, 1], [1, 1, 0]) {
        my @fractions = map 'Number::Fraction'->new(@cols[@$_]),
            [-18, -14],[-12, -8], [-6, -2];
        my @newcols;
        for my $i (0 .. 2) {
            if ($modify->[$i]) {
                $fractions[$i] += 'Number::Fraction'->new(1, 2);
                $fractions[$i] -= 1 if $fractions[$i] >= 1;
            }
            push @newcols, $fractions[$i];
        }
        s{/}{ / }, s{^0$}{0 / 1} for @newcols;  # Format the fractions.
        print @cols[0..19], $newcols[0],
              $cols[25],    $newcols[1],
              $cols[31],    $newcols[2],
              "\n";
    }

}