如何在shell脚本中打印特定列的数量?

时间:2013-04-25 10:59:07

标签: unix sed awk

我有一个文本文件temp1,并说它有超过20列,它的数值如下所示,

1,0,3,0,5........,
1,0,5,0,8........,
3,0,6,0,3........,
5,0,6,0,4........,
.................,

我想删除总数(总和)为零的列,我需要将剩余列重定向到新文件

ie:例如,如上面的第2和第4列总共为零,所以我需要删除第2和第4列并将其重定向到单独的文件。

任何人都可以帮我吗?

4 个答案:

答案 0 :(得分:2)

$ cat file
1,0,3,0,5
1,0,5,0,8
3,0,6,0,3
5,0,6,0,4

$ awk -f tst.awk file
1,3,5
1,5,8
3,6,3
5,6,4

$ cat tst.awk
BEGIN{ FS="," }
{
    for (j=1;j<=NF;j++) {
        val[NR,j] = $j
        sum[j] += val[NR,j]
    }
}
END {
    for (i=1;i<=NR;i++) {
        ofs = ""
        for (j=1;j<=NF;j++) {
            if (sum[j]) {
                printf "%s%s",ofs,val[i,j]
                ofs = FS
            }
        }
        print ""
    }
}

答案 1 :(得分:1)

你可以使用awk :(以下是丑陋的但我希望可读。这就是目标。我让awkist更好地进一步增强/减少它)

如果数据在文件/path/to/zefile中:

awk -F',' '  
      FNR==NR { for (col=1;col<=NF;col++)
                   { if ($col != 0) 
                        {wewantthiscolumn[col]=1 } 
                   }
                next
              }

              { for (col=1;col<=NF;col++) 
                   { if (wewantthiscolumn[col]==1) 
                        { printf ("%s,",$col) } 
                   }
                print "" 
              }' /path/to/zefile /path/to/zefile | sed -e 's/,$//'

这个想法:我们在/ path / to / zefile / path / to / zefile上启动awk(因此,它读取了两次)。

在第一遍中,我们创建了一个“wewantthiscolumn”数组。一旦该列与0不同,该数组就包含“1”。“next”确保我们只在FNR(= CURRENT文件中的行数)== NR(=总行数)时执行此操作,这只在第一次通过时才是真的。

在第二遍(因此我们直接转到第二个{},现在是NR&gt; FNR):我们只显示具有相应$col的列值wewantthiscolumn(col)==1,然后是“ ,“(所以有一点问题:最后一个col会有一个”,“之后)

然后我们通过sed传递这个以消除“,$”位。

我不确定没有更好的方法:awk可以删除一个字段吗?所以它可以删除第二遍的字段col?然后打印结果$ 0会更容易,设置OFS=','以将它们与,分开...

这将成为第二遍:

 awk -F',' '  
      FNR==NR { for (col=1;col<=NF;col++)
                   { if ($col != 0) 
                        {wewantthiscolumn[col]=1 } 
                   }
                next
              }

              { for (col=1;col<=NF;col++) 
                   { if (wewantthiscolumn[col]==0) 
                        $col="DELETETHIS"
                   }
                gensub(",DELETETHIS","",g)
                gensub("DELETETHIS,","",g)
                print $0 
              }' /path/to/zefile /path/to/zefile

我不想假设没有列可以为空,因此我使用“DELETETHIS”来确保我只删除相关字段...但这意味着第一种方式实际上更简单^^:只打印字段你需要,然后摆脱行尾的“,”。

答案 2 :(得分:1)

这是使用awk的一种方式。像:

一样运行
awk -f ./script.awk file{,}

script.awk的内容:

BEGIN {
    FS=","
}

FNR==NR {
    for(i=1;i<=NF;i++) {
        if ($i != 0) {
            a[i]
        }
    }
    next
}

{
    for(j=1;j<=NF;j++) {
        if (j in a) {
            printf "%s%s", $j, (j==NF ? RS : FS)
        }
    }
}

或者,这是单行:

awk -F, 'FNR==NR { for(i=1;i<=NF;i++) if ($i != 0) a[i]; next } { for(j=1;j<=NF;j++) if (j in a) printf "%s%s", $j, (j==NF ? RS : FS) }' filex{,}

file的内容:

1,0,3,0,5,0
1,0,5,0,8,1
3,0,6,0,3,2
5,0,6,0,4,5

结果:

1,3,5,0
1,5,8,1
3,6,3,2
5,6,4,5

答案 3 :(得分:0)

使用python的解决方案:

#!/usr/bin/env python

def transpose(grid):
    return zip(*grid)

def removeBlankRows(grid):
    return [list(row) for row in grid if any(map(int,row))]

grid = []
with open("input.csv") as fd:
    for line in fd:
        grid.append(line.strip().split(','))

data = removeBlankRows(transpose(removeBlankRows(transpose(grid))))

for i in data:
    print ",".join(i)

输入:

1,0,3,0,5
1,0,5,0,8
3,0,6,0,3
5,0,6,0,4

输出:

1,3,5
1,5,8
3,6,3
5,6,4

输入:

1,0,3,0,5
1,0,5,0,8
3,0,6,0,3
5,0,6,1,4

输出:

1,3,0,5
1,5,0,8
3,6,0,3
5,6,1,4