如何通过匹配标题拆分列?

时间:2016-03-04 09:22:33

标签: linux awk

我在想是否有办法通过匹配标题来拆分列?

数据看起来像这样

         ID_1   ID_2   ID_3   ID_6   ID_15
value1   0      2      4      7      6
value2   0      4      4      3      8
value3   2      2      3      7      8

我想只在ID_3和& ID_15

ID_3   ID_15
4      6
4      8
3      8
如果我知道列的顺序,awk可以简单地将它分开 但是,我有一张非常庞大的桌子,只有手中的ID列表 我仍然可以使用awk或者在linux中有一种更简单的方法吗?

4 个答案:

答案 0 :(得分:1)

$ cat c.awk
NR == 1 {
    for (i=1; i<=NF; ++i) {
        if ($i == "ID_3") col_3 = (i + 1)
        if ($i == "ID_15") col_15 = (i + 1)
    }
    print "ID_3", "ID_15"
}

NR > 1 { print $col_3, $col_15 }


$ awk -f c.awk c.txt
ID_3 ID_15
4 6
4 8
3 8

答案 1 :(得分:1)

你可以选择这样的东西:

BEGIN { 
    keys["ID_3"]
    keys["ID_15"] 
}

NR == 1 { 
    for (i = 1; i <= NF; ++i) 
        if ($i in keys) cols[++n] = i 
}

{ 
    for (i = 1; i <= n; ++i) 
        printf "%s%s", $(cols[i]+(NR>1)), (i < n ? OFS : ORS) 
}

将脚本保存到文件并像awk -f script.awk file一样运行。

或者,作为&#34;单行&#34;:

awk 'BEGIN { keys["ID_3"]; keys["ID_15"] } 
NR == 1 { for (i = 1; i <= NF; ++i) if ($i in keys) cols[++n] = i }
{ for (i = 1; i <= n; ++i) printf "%s%s", $(cols[i]+(NR>1)), (i < n ? OFS : ORS) }' file

在处理文件之前,密钥在keys数组中设置,对应于感兴趣的列标题。

在第一行,记录包含cols数组中某个键的所有列号。

循环遍历每个列并打印出来,然后输出字段分隔符OFS或输出记录分隔符ORS,具体取决于它是否是最后一个。 $(cols[i]+(NR>1))处理第一个行之后的行在开头有一个额外字段的事实,因为NR>1对于那些行是真(1)而对于第一行是假(0)。

答案 2 :(得分:1)

输入格式定义不明确,但有一些简单的方法,awkperlsqlite

(FNR==1) {
    nocol=split(col,ocols,/,/)    # cols contains named columns
    ncols=split("vals " $0,cols)  # header line
    for (nn=1; nn<=ncols; nn++) colmap[cols[nn]]=nn  # map names

    OFS="\t"                      # to align output
    for (nn=1; nn<=nocol; nn++) printf("%s%s",ocols[nn],OFS)
    printf("\n")                  # output header line 
}
(FNR>1) { # read data
    for (nn=1; nn<=nocol; nn++)  {
        if (nn>1) printf(OFS)     # pad
        if (ocols[nn] in colmap) { printf("%s",$(colmap[ocols[nn]])) }
        else                     { printf "--" } # named column not in data
    }  
    printf("\n") # wrap line
}

$ nawk -f mycols.awk -v col=ID_3,ID_15 data
ID_3    ID_15   
4       6
4       8
3       8

Perl,只是上面的一个变体与一些perl成语混淆/娱乐:

use strict;
use warnings;

our @ocols=split(/,/,$ENV{cols}); # cols contains named columns
our $nocol=scalar(@ocols);
our ($nn,%colmap);
$,="\t";                          # OFS equiv

# while (<>) {...} implicit with perl -an
if ($. == 1) {  # FNR equiv
    %colmap = map { $F[$_] => $_+1 } 0..$#F ; # create name map hash
    $colmap{vals}=0;                          # name anon 1st col
    print @ocols,"\n";                        # output header
} else {
    for ($nn = 0; $nn < $nocol; $nn++) {
       print "\t" if ($nn>0);
       if (exists($colmap{$ocols[$nn]})) { printf("%s",$F[$colmap{$ocols[$nn]}]) }
       else                              { printf("--") } # named column not in data 
    }
    printf("\n")
}

$ cols="ID_3,ID_15" perl -an mycols.pl < data

使用环境变量来跳过解析命令行的工作。它需要perl选项-an来设置字段分割和输入读取循环(就像awk一样)。

使用sqlite(我使用v3.11,v3.8或更高版本是有用的.import我需要的)。这使用内存中的临时数据库(如果文件对于内存来说太大,或者对于已解析数据的持久副本,则称为文件),并根据第一行自动创建表。这里的优点是您可能根本不需要任何脚本,并且您只需一次解析就可以对数据执行多次查询。

如果您有一个硬标签分隔列,则可以跳过此下一步,在这种情况下,在下面的sqlite示例中将.mode csv替换为.mode tab。 否则,要将数据转换为合适的CSV-ish格式:

nawk -v OFS="," '(FNR==1){$0="vals " $0} {$1=$1;print} < data > data.csv

这会在第一行添加一个虚拟的第一列“vals”,然后以逗号分隔的方式打印每一行,它通过对$1的看似毫无意义的赋值来完成此操作,但这会导致$0重新计算用OFS(逗号)替换FS(空格/制表符)。

$ sqlite3
sqlite> .mode csv 
sqlite> .import data.csv mytable
sqlite> .schema mytable
CREATE TABLE mytable(
  "vals" TEXT,
  "ID_1" TEXT,
  "ID_2" TEXT,
  "ID_3" TEXT,
  "ID_6" TEXT,
  "ID_15" TEXT
);
sqlite> select ID_3,ID_15 from mytable;
ID_3,ID_15
4,6
4,8
3,8
sqlite> .mode column
sqlite> select ID_3,ID_15 from mytable;
ID_3        ID_15     
----------  ----------
4           6         
4           8         
3           8         

使用.once.output将输出发送到文件(sqlite docs)。根据需要使用.headers on.headers off。 sqlite非常乐意创建一个未命名的列,因此您不必在标题行的第一列添加名称,但您需要确保所有输入行和格式的列数相同。

如果在.import期间出现“预期X列但发现Y”错误,那么您需要稍微清理数据格式。

答案 3 :(得分:0)

尝试以下脚本:

 #!/bin/sh

file="$1"; shift

awk -v cols="$*" '
BEGIN{
split(cols,C)
OFS=FS="\t"
getline
split($0,H)
for(c in C){
    for(h in H){
        if(C[c]==H[h])F[i++]=h
    }
}
}
{ l="";for(f in F){l=l $F[f] OFS}print l }

' "$file"

在命令行类型中:

[sumit.gupta@rpm01 ~]$ test.sh filename ID_3 ID_5