补充模式

时间:2012-01-05 00:22:46

标签: linux bash shell awk

我在文件中有这些记录:

1867    121 2 56 
1868    121 1 6 
1868    121 2 65 
1868    122 0 53 
1869    121 0 41 
1869    121 1 41 
1871    121 1 13 
1871    121 2 194

我想得到这个输出:

1867    121 2 56 
1868    121 1 6 
1868    121 2 65 
1868    122 0 53 
1869    121 0 41 
1869    121 1 41 
1870    121 0 0
1871    121 1 13 
1871    121 2 194

区别在于1870 121 0 0行。

因此,如果第一列中的数字之间的差异大于1,那么我们必须包含缺少数字的行(上面的情况是1870)和其他列。一个人应该以某种方式得到其他列,让第二列成为列数的可能值的最小值(在示例中,这些值可能是121122),并且与第三列情况相同。最后一列的值总是为零。

有人可以给我一些建议吗?提前谢谢!

我试图用awk来解决它,但也许还有其他更好或更实用的解决方案......

4 个答案:

答案 0 :(得分:2)

这样的事情可以起作用 -

awk 'BEGIN{getline;a=$1;b=$2;c=$3}
NR==FNR{if (b>$2) b=$2; if (c>$3) c=$3;next} 
{if ($1-a>1) {x=($1-a); for (i=1;i<x;i++) {print (a+1)"\t"b,c,"0";a++};a=$1} else a=$1;print}' file file

说明:

  1. BEGIN{getline;a=$1;b=$2;c=$3} -

    在此 BEGIN 块中,我们读取了第一行,并将column 1中的值分配给变量a,将column 2分配给变量{{1 }和b变量column 3

  2. c -

    在此我们浏览整个文件(NR==FNR{if (b>$2) b=$2; if (c>$3) c=$3;next})并跟踪NR==FNRcolumn 2中可能的最低值,并将其存储在变量column 3和{分别为{1}}。我们使用b来避免运行第二个c语句。

  3. next -

    pattern{action}语句检查{if ($1-a>1) {x=($1-a); for (i=1;i<x;i++) {print (a+1)"\t"b,c,"0";a++};a=$1} else a=$1;print}中的值,并将其与action进行比较。如果差异大于1,我们会column 1添加所有缺失的行,并将a的值设置为for loop。如果连续行上a中的值不大于1,我们会将$1的值分配给column 1column 1

  4. 测试:

    a

答案 1 :(得分:1)

Perl解决方案。也应该适用于大文件,因为它不会将整个文件加载到内存中,而是遍历文件两次。

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

my $file = shift;

open my $IN, '<', $file or die $!;

my @mins;
while (<$IN>) {
    my @cols = split;
    for (0, 1) {
        $mins[$_] = $cols[$_ + 1] if $cols[$_ + 1] < $mins[$_ ]
                                     or ! defined $mins[$_];
    }
}

seek $IN, 0, 0;
my $last;
while (<$IN>) {
    my @cols = split;
    $last //= $cols[0];

    for my $i ($last .. $cols[0]-2) {
        print $i + 1, "\t@mins 0\n";
    }
    print;
    $last = $cols[0];
}

答案 2 :(得分:1)

Bash解决方案:

# initialize minimum of 2. and 3. column
read no min2 min3 c4 < "$infile"

# get minimum of 2. and 3. column
while read c1 c2 c3 c4 ; do
  [ $c2 -lt $min2 ] && min=$c2
  [ $c3 -lt $min3 ] && min=$c3
done < "$infile"

while read c1 c2 c3 c4 ; do
  # insert missing line(s) ?
  while (( c1- no > 1 )) ; do
    ((no++))
    echo -e "$no $min2 $min3 0"
  done
  # now insert existing line
  echo -e "$c1 $c2 $c3 $c4"
  no=$c1
done < "$infile"

答案 3 :(得分:0)

使用awk的一种方式:

BEGIN { 
        if ( ARGC > 2 ) {
                print "Usage: awk -f script.awk <file-name>"
                exit 0
        }

        ## Need to process file twice, duplicate the input filename.
        ARGV[2] = ARGV[1]
        ++ARGC

        col2 = -1
        col3 = -1
}

## First processing of file. Get min values of second and third columns.
FNR == NR {
        col2 = col2 < 0 || col2 > $2 ? $2 : col2
        col3 = col3 < 0 || col3 > $3 ? $3 : col3
        next
}

## Second processing of file.
FNR < NR {
        ## Get value of column 1 in first row.
        if ( FNR == 1 ) {
                col1 = $1
                print
                next
        }

        ## Compare current value of column 1 with value of previous row.
        ## Add a new row while difference is bigger than '1'.
        while ( $1 - col1 > 1 ) {
                ++col1
                printf "%d\t%d %d %d\n", col1, col2, col3, 0
        }

        ## Assing new value of column 1.
        col1 = $1
        print
}

运行脚本:

awk -f script.awk infile

结果:

1867    121 2 56 
1868    121 1 6 
1868    121 2 65 
1868    122 0 53 
1869    121 0 41 
1869    121 1 41 
1870    121 0 0
1871    121 1 13 
1871    121 2 194