簡體   English   中英

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

[英]Extract columns from tab separated file

我有一個格式如下的文件( 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 ,其中要選擇的列來自定義為COLUMN_LIST=$@的 shell 變量,其中$@是傳遞給我的 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.

在這種情況下,我會對列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 -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以表格格式格式化輸出。

解決這個問題的最好方法是創建一個數組(下面的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

你可以用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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM