从制表符分隔的文件中提取列

时间:2019-05-14 13:59:56

标签: awk rdb

我有一个文件(data.rdb),格式如下:

col1    col2    col3    col4    col5    col6    col7
aaa1    bbb1    ccc1    ddd1    eee1    fff1    ggg1
aaa2    bbb2    ccc2    ddd2    eee2    fff2    ggg2
aaa3    bbb3    ccc3    ddd3    eee3    fff3    ggg3

数据的某些属性:

  • 所有列都用制表符分隔
  • 列的宽度不同
  • 单元格的长度可能不同
  • 文件中的列将比显示的要多得多,并需要几百行
  • 我提供的列名只是通用的,真实名称可以是任何单词,没有制表符,空格或特殊字符。

我需要使用bash按名称提取某些列,例如col1col3col6,其中要选择的列来自定义为{{ 1}},其中COLUMN_LIST=$@是传递给我的Shell脚本的参数。每次我调用脚本时,参数的数量和名称可能会更改。

该脚本必须为bash,不能为python或类似版本。

有什么想法吗?我曾考虑过使用$@ / awk,但不知道如何通过列名进行选择。列顺序可能因文件而异。

谢谢 豪尔赫

更新

由于某种原因,这些解决方案似乎都无法在我的真实数据文件上运行(即,我完全没有输出),因此我发布了其中一个的子集:

gawk

在这种情况下,我将对列date star jdb texp 2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 HD217987 2400000.23551544 900. 2013-11-22 TOI-134 2400000.23551544 900. 2013-11-22 tauCet 2400000.23551544 60. 2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300. starjdb

感兴趣

更新2

我使用了@EdMorton的代码,这是结果:

texp

更新3

我最终使用了EdMorton的date star jdb texp date star jdb texp 2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 epsInd 2400000.23551544 100. 2013-11-22 HD217987 2400000.23551544 900. 2013-11-22 HD217987 2400000.23551544 900. 2013-11-22 TOI-134 2400000.23551544 900. 2013-11-22 TOI-134 2400000.23551544 900. 2013-11-22 tauCet 2400000.23551544 60. 2013-11-22 tauCet 2400000.23551544 60. 2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300. 2013-11-22 BD+01316 2400000.23551544 300. 版本-主要是为了输出的灵活性-但由于修改,我不希望它输出错误的列:

awk

我得到的主要问题是标题行没有制表符分隔,因此列细分无法正常工作。识别制表符/非制表符的简单方法:

BEGIN {
    numCols = split(column_list,cols)
    OFS="\t"
}
{ sub(/\r$/,"") }
NR==1 {
    for (fldNr=1; fldNr<=NF; fldNr++) {
        f[$fldNr] = fldNr
    }
}
{
    for (colNr=1; colNr<=numCols; colNr++) {
        colName = cols[colNr]
        colVal  = (colName in f ? $(f[colName]) : "")
        printf "%s%s", colVal, (colNr<numCols ? OFS : ORS)
    }
}

给出了我的一个测试文件:

tr $'\t' '#' < data.rdb | head -2

3 个答案:

答案 0 :(得分:3)

  

列顺序可能因文件而异。

您可以使用awk来使用此方法,该方法以空格分隔的标题列名称作为输入,然后通过处理第一条记录将其首先转换为列号。一旦检索到所需的列号,我们就从下一行开始打印它们。

awk -v cols='col1 col3 col6' 'BEGIN {
   FS=OFS="\t"
   n = split(cols, a, " ")
   for (i=1; i <= n; i++)
      c[a[i]]
}
{
   sub(/\r$/, "")
}
NR == 1 {
   for (i=1; i<=NF; i++)
      if ($i in c)
         hdr[i]
}
{
   for (i=1; i<=NF; i++)
      if (i in hdr)
         s = sprintf(s "%s%s", OFS, $i)
   sub(OFS, "", s)
   print s
   s =""
} ' file | column -t

star      jdb               texp
epsInd    2400000.23551544  100.
epsInd    2400000.23551544  100.
epsInd    2400000.23551544  100.
HD217987  2400000.23551544  900.
TOI-134   2400000.23551544  900.
tauCet    2400000.23551544  60.
BD+01316  2400000.23551544  300.
BD+01316  2400000.23551544  300.
BD+01316  2400000.23551544  300.
BD+01316  2400000.23551544  300.

PS:添加了column -t以便以表格格式格式化输出。

答案 1 :(得分:1)

处理此问题的最佳方法是创建一个数组(下面的f[]),在读取标题行时将列标题字符串(即字段名称)映射到字段编号,然后仅访问字段从那时起他们的名字。

已更新,以防止调用方要求输入不存在的列名和DOS行尾:

$ cat tst.awk
BEGIN {
    numCols = split(column_list,cols)
    FS=OFS="\t"
}
{ sub(/\r$/,"") }
NR==1 {
    for (fldNr=1; fldNr<=NF; fldNr++) {
        f[$fldNr] = fldNr
    }
}
{
    for (colNr=1; colNr<=numCols; colNr++) {
        colName = cols[colNr]
        colVal  = (colName in f ? $(f[colName]) : (NR>1 ? "N/A" : colName))
        printf "%s%s", colVal, (colNr<numCols ? OFS : ORS)
    }
}

$ awk -v column_list='col1 col3 col6' -f tst.awk data.rdb
col1    col3    col6
aaa1    ccc1    fff1
aaa2    ccc2    fff2
aaa3    ccc3    fff3

$ awk -v column_list='col1 col3 col6 bob' -f tst.awk data.rdb
col1    col3    col6    bob
aaa1    ccc1    fff1    N/A
aaa2    ccc2    fff2    N/A
aaa3    ccc3    fff3    N/A

请注意,采用上述方法,您可以更改输出列的顺序,而不仅仅是按原始顺序打印它们:

$ awk -v column_list='col5 col2 col4' -f tst.awk data.rdb
col5    col2    col4
eee1    bbb1    ddd1
eee2    bbb2    ddd2
eee3    bbb3    ddd3

答案 2 :(得分:0)

您可以使用coreutils进行操作。假设您有一个文件callef cols,其中包含所需的列,例如:

col2
col3
col6

您可以像这样提取列号:

head -n1 infile | tr '\t' '\n' | grep -nf cols | cut -d: -f1 | paste -sd,

输出:

2,3,6

将此内容传递给cut,例如:

cut -f $(head -n1 infile | tr '\t' '\n' | grep -nf cols | cut -d: -f1 | paste -sd,) infile

输出:

col2    col3    col6
bbb1    ccc1    fff1
bbb2    ccc2    fff2
bbb3    ccc3    fff3