首页 > 解决方案 > 从制表符分隔的文件中提取列

问题描述

我有一个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其中要选择的列来自一个 shell 变量,该变量定义为传递给我的 shell 脚本的参数COLUMN_LIST=$@在哪里。$@每次调用脚本时,参数的数量和名称可能会发生变化。

该脚本需要在 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.

在这种情况下,我会对列star jdbtexp

更新 2

我使用了@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.

更新 3

我最终使用了 EdMorton 的版本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

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

date    star    jdb texp
2013-11-22#epsInd#2400000.23551544#100.

标签: awk

解决方案


列顺序可能会因文件而异。

您可以使用这种方法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到表格格式的格式输出。


推荐阅读